How to pass array'method signature C# - c#

Now I have a ConcurrentDictionary .
I want to invoke the IDataExchangeServiceCallBack's method.
the IDataExchangeServiceCallBack's code below:
[ServiceContract]
public interface IDataExchangeServiceCallBack
{
[OperationContract(IsOneWay = true)]
void SendResult(string msg);
[OperationContract(IsOneWay = true)]
void Receive(List<RealDataModel> models);
}
In other class, I want invoke dict's method foreach.
such as
public void Receive(List<RealDataModel> models)
{
Broast(o => nameof(o.Receive), models);
}
public void SendResult(string msg)
{
Broast(o => nameof(o.SendResult), msg);
}
And Broast method below:
private void Broast(Func<IDataExchangeServiceCallBack, string> funcMethodName, params object[] args)
{
if (_callbackChannelList.Count > 0)
{
var callbackChannels = _callbackChannelList.ToArray();
foreach (var channel in callbackChannels)
{
try
{
var type = channel.Value.GetType();
// fetch the method's name.
var methodName = funcMethodName.Invoke(channel.Value);
// reflect & get the method
var methodInfo = type.GetMethod(methodName);
//invoke
methodInfo?.Invoke(channel.Value, args);
}
catch (Exception ex)
{
_callbackChannelList.TryRemove(channel.Key, out _);
}
}
}
}
Now my question is how to implement above code without reflect, it's there any better solution.
The Expression can implement it ?
I'm not familiar with Expression.
Thanks.

I don't understand how you got to the solution you have. Regardless of the type of o, the expression, for example, nameof(o.Receive) will always result in the string "Receive". It seems to me you could just pass nameof(IDataExchangeServiceCallBack.Receive) to the method instead of passing a delegate.
That said, I also don't understand why you are looking to use reflection or Expression. It appears to me that, at the call site, you know the type of object you're dealing with, the method you want to call, and the arguments you want to pass. So you could just pass a delegate that does all that. For example:
private void Broast(Action<IDataExchangeServiceCallBack> callback)
{
foreach (var channel in _callbackChannelList.ToArray())
{
try
{
//invoke
callback(channel.Value);
}
catch (Exception ex)
{
_callbackChannelList.TryRemove(channel.Key, out _);
}
}
}
Used like:
public void Receive(List<RealDataModel> models)
{
Broast(o => o.Receive(models));
}
public void SendResult(string msg)
{
Broast(o => o.SendResult(msg));
}
Note that I've cleaned up the Broast() method a bit. There's very little overhead calling ToArray() on an empty collection, and it simplifies the code significantly to remove the Count > 0 check. Code is always easier to write and read later if you can remove unnecessary conditional checks.

Related

Is it possible to infer this generic type, for type-safe callback?

Is there a way to get rid of the CS0411 error below, and not have to explicitly state the type?
Also do not want to have to use reflection.
var router = new ExampleRouter();
var controller = new ExampleWebController();
// compiles, but not elegant
router.MapPost<string>("/api/bar", controller.ProcessString);
// error CS0411: can't infer type
router.MapPost("/api/foo", controller.ProcessString);
class ExampleWebController {
public ExampleWebController() { }
public bool ProcessNumber(int v) { return true; }
public bool ProcessString(string v) { return true; }
}
class ExampleRouter {
public ExampleRouter() { }
public void MapPost<TBody>(string path, Func<TBody, bool> handler) {
// Save typeof(TBody), since TBody will actually be a class type we
// will construct for each callback
var body_type = typeof(TBody);
}
}
Yep, as someone's mentioned in comments one solution is to pass in the data as a parameter:
public void MapPost<TBody>(string path, Func<TBody, bool> handler, Tbody data) {
object dataType = data.GetType();
}
The reason your code is "inelegant" as you've said, is because the order of your generic arguments specifies an input type (TBody) and an output type (bool). However, in your calls to MapBody, you are only providing methods that return boolean results, so that the compiler doesn't know what to use for the value of TBody.
This is the origin of the CS0411 error you are receiving. The only way around it is to provide a generic type argument at the point of call.
This is why this code works, and should be what you use going forward:
var router = new ExampleRouter();
var controller = new ExampleWebController();
// compiles, but not elegant
router.MapPost<string>("/api/bar", controller.ProcessString);
A bit of a self answer here. If I change it to this, the MapPost() code looks elegant, which was my goal. HOWEVER, I have lost some compile time checking -- for example anything can be passed in as a "handler". I will post a new question on how I refine this.
var router = new ExampleRouter();
var controller = new ExampleWebController();
// We will have to do runtime validation that controller.ProcessString is a
// legal callback (which isn't ideal, but still fine).
// An improvement would be to add some kind of generic constraints?
router.MapPost("/api/foo", controller.ProcessString);
class ExampleWebController {
public ExampleWebController() { }
public bool ProcessNumber(int v) { return true; }
public bool ProcessString(string v) { return true; }
}
class ExampleRouter {
public ExampleRouter() { }
public void MapPost<TFunc>(string path, TFunc handler) {
var func_type = typeof(TFunc);
Console.WriteLine(func_type); // Prints "System.Func"
var args = func_type.GetGenericArguments();
foreach (var arg in args) {
// Prints "System.String", "System.Boolean"...awesome
Console.WriteLine(arg);
}
}
}

Clarification on c# delegates / methods

I've used delegates in the past so I'm familiar with their use and benefits. I've also done a lot of reading/research, but I'm trying to wrap my head around this and getting nowhere. I'd like to use a delegate (I believe) to encapsulate some code or use a delegate within a method to call some outside code.
I'm using the same code in 20 different places to wrap an entity framework update in a transaction. I would like the code in one place; however, I can't do it in a method by itself because there is a middle part that will change each time. I'm looking for some ideas / clarification on how I can best do this (.net 3.5, ms sql 2010). - thnx
code sample:
void AddItem(string objDetails)
{
// setup method specific entity objects
SomeObject obj = new SomeObject { Details = objDetails };
//////// Begin transaction code that I would like to encapsulate ///////
bool success = false;
using (Entities data = new Entities())
{
for (int i = 0; i < s.BaseSettings.CommandRetries; i++)
{
using (TransactionScope transaction = new TransactionScope())
{
try
{
//////////////////////////////////////////////////////////////
///////// BEGIN Code that I would like to change / call different each time ////////
data.AddToSOMEOBJECTs(obj);
//////////////// END //////////////////////////////////
//// RETURN TO ENCAPSULATED CODE ////
data.SaveChanges(false);
transaction.Complete();
success = true;
break;
}
catch (Exception ex)
{
if (ex.GetType() != typeof(UpdateException))
{
throw new Exception("Unhandled db exception.");
}
}
}
}
if (success)
{
data.AcceptAllChanges();
}
else
{
throw new Exception();
}
}
}
You pass the function a delegate (or lambda) that does the custom bit
like this
void AddItem<T>(string objDetails, Func<T> custom) {
.
. common
.
.
T someReturn = custom();
.
. common
.
}
add call like this perhaps:
Func<int> custom = () => {
// do something custom
return 9;
}
// Call common function
AddItem<int>(..., custom);
All that matters is that the interface for the Func matches what you need.
You can pass different methods by using a delegate and an event. Here's an example of a class that does this:
class SampleClass
{
public delegate void TransactionDelegate();
public event TransactionDelegate MyTransactionDelegate;
public void DoSomething()
{
MyTransactionDelegate();
}
}
You can then use a lambda expression to pass methods to the event like this:
class MainClass
{
public static void Main (string[] args)
{
var test = new SampleClass();
test.MyTransactionDelegate += () => {Console.WriteLine("Success");};
test.DoSomething();
}
}

Pass delegate together with parameter to a function

I want enqueue a list of tasks and then perform on certain event. Code:
internal class MyClass
{
private Queue<Task> m_taskQueue;
protected MyClass()
{
m_taskQueue = new Queue<Task>();
}
public delegate bool Task(object[] args);
public void EnqueueTask(Task task)
{
m_taskQueue.Enqueue(task);
}
public virtual bool Save()
{
// save by processing work queue
while (m_taskQueue.Count > 0)
{
var task = m_taskQueue.Dequeue();
var workItemResult = task.Invoke();
if (!workItemResult)
{
// give up on a failure
m_taskQueue.Clear();
return false;
}
}
return true;
}
}
Each delegate task may have their own list of parameters: Task(object[] args). My question is how to pass the parameter to each task for the task queue?
Okay, now we have a bit more information, it sounds like your EnqueueTask method should actually look like this:
public void EnqueueTask(Task task, object[] values)
Right?
For starters I would avoid using the name Task, which is already part of the core of .NET 4 and will become very prominent in .NET 5. As Joshua said, you've basically got a Func<object[], bool>.
Next, you could keep two lists - one for the delegates and one for the values, but it's easier just to keep a Queue<Func<bool>> like this:
private readonly Queue<Func<bool>> taskQueue = new Queue<Func<bool>>();
public void EnqueueTask(Task task, object[] values)
{
taskQueue.Enqueue(() => task(values));
}
Then the rest of your code will actually work "as is". The lambda expression there will capture values and task, so when you invoke the Func<bool>, it will supply those values to the original delegate.
Provided understanding your question correctly you just pass the information like a normal call. Have you considered using Func? You can just pass arguments to the Task.Invoke i.e. Task.Invoke([arguments here as a *single* object array]).
object[] arguments = null; // assign arguments to something
var workItemResult = task.Invoke(arguments);
Below is an example with the Func type.
internal class MyClass
{
private Queue<Func<object[], bool>> m_taskQueue;
protected MyClass()
{
m_taskQueue = new Queue<Func<object[], bool>>();
}
public void EnqueueTask(Func<object[], bool> task)
{
m_taskQueue.Enqueue(task);
}
public virtual bool Save()
{
object[] arguments = null; // assign arguments to something
// save by processing work queue
while (m_taskQueue.Count > 0)
{
var task = m_taskQueue.Dequeue();
var workItemResult = task(arguments);
if (!workItemResult)
{
// give up on a failure
m_taskQueue.Clear();
return false;
}
}
return true;
}
}

Strongly typed way of storing type references

I need to store a collection of types.
All types implement the same interface IHandler<T>, where T is a parameter of the parent class.
At runtime, I enumerate the list of handlers and process a message. Each handler is created by a builder (just uses StructureMap internally). The builder exposes a method like:
static void DoSomething<T>(Action<T> action)
{
}
Of course, I only have a Type so can't use the above.
I've got round this by also passing the underlying interface as the generic parameter and the concrete type as a parameter:
DoSomething<IHandler<T>>(handlerType, h =>
{
h.Handle(message);
});
Then inside DoSomething I can get an instance of handlerType but cast it as IHandler<T>.
Just wondered if there was a better/cleaner way.
Update
In response to some of the comments.
The collection is an ICollection<Type>, not instances. The message handlers are created on demand, on different threads, for each batch of messages, so creating the handlers in advance, or using Lazy<T>, was not an option.
Essentially I am trying to abstract away some direct references to StructureMap. Specifically, DoSomething<T> actually creates the handler using a nested container, before executing the action (it's Handle method).
Update 2 (solution)
I realized that I could handle (no pun intended) this better by storing a collection of Action<T> and creating the handlers using a factory. Here's a simple example:
public class SimpleProcessor<T> where T : IMessage
{
ICollection<Action<T>> handlers;
T message;
public SimpleProcessor(T message)
{
this.handlers = new List<Action<T>>();
this.message = message;
}
public void AddHandler(Action<T> handler)
{
handlers.Add(handler);
}
public void Process()
{
foreach (var handler in handlers)
{
handler(message);
}
}
}
Usage:
var testMessage = new TestMessage { Message = "Foo" };
var simpleProcessor = new SimpleProcessor<TestMessage>(testMessage);
simpleProcessor.AddHandler(m => DoSomething<TestMessageHandler>(h => h.Handle(m)));
simpleProcessor.Process();
I'm more or less happy with this solution.
If you're willing to change Action<T> into Action<dynamic>, then you can do something like this:
class Program
{
static void Main(string[] args)
{
try
{
var myMessage = new object();
Action<dynamic> action = (dynamic h) => { h.Handle(myMessage); };
Type myType = typeof(int);
var method = typeof(Program).GetMethod("DoSomething");
var concreteMethod = method.MakeGenericMethod(myType);
concreteMethod.Invoke(null, new [] { action });
Console.ReadKey();
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
Console.ReadKey();
}
}
static public void DoSomething<T>(Action<dynamic> action)
{
Console.WriteLine("DoSomething invoked with T = " + typeof(T).FullName);
}
}

Anonymous method as parameter to BeginInvoke?

Why can't you pass an anonymous method as a parameter to the BeginInvoke method? I have the following code:
private delegate void CfgMnMnuDlg(DIServer svr);
private void ConfigureMainMenu(DIServer server,)
{
MenuStrip mnMnu = PresenterView.MainMenu;
if (mnMnu.InvokeRequired)
{
mnMnu.BeginInvoke((CfgMnMnuDlg)ConfigureMainMenu,
new object[] { server});
}
else
{
// Do actual work here
}
}
I'm trying to avoid declaring the delegate. Why can't I write something like the below instead? Or can I, and I just can't figure out the correct syntax? The below currently generates an:
Argument type 'Anonymous method' is not assignable to parameter type 'System.Delegate'
Ok, that's right of course, but is there some other syntax I can use to do this (avoid having to declare a separate delegate in order to use BeginInvoke()?
(Being able to do this would fit in neatly with the concept of using anon methods/lamdas in place of explicit delegates which works so cleanly everywhere else.)
private void ConfigureMainMenu(DIServer server,)
{
MenuStrip mnMnu = PresenterView.MainMenu;
if (mnMnu.InvokeRequired)
{
mnMnu.BeginInvoke( // pass anonymous method instead ?
delegate(DIServer svr) { ConfigureMainMenu(server);},
new object[] { server});
}
else
{
// Do actual work here
}
}
Try this:
control.BeginInvoke((MethodInvoker) delegate { /* method details */ });
Or:
private void ConfigureMainMenu(DIServer server)
{
if (control.InvokeRequired)
{
control.BeginInvoke(new Action<DIServer >(ConfigureMainMenu), server);
}
else
{
/* do work */
}
}
Or:
private void ConfigureMainMenu(DIServer server)
{
MenuStrip mnMnu = PresenterView.MainMenu;
if (mnMnu.InvokeRequired)
{
// Private variable
_methodInvoker = new MethodInvoker((Action)(() => ConfigureMainMenu(server)));
_methodInvoker.BeginInvoke(new AsyncCallback(ProcessEnded), null); // Call _methodInvoker.EndInvoke in ProcessEnded
}
else
{
/* do work */
}
}
You should be able to write something like this:
private void ConfigureMainMenu(DIServer server,)
{
MenuStrip mnMnu = PresenterView.MainMenu;
if (mnMnu.InvokeRequired)
{
mnMnu.BeginInvoke(new Action<DIServer>(ConfigureMainMenu),
new object[] { server});
}
else
{
// Do actual work here
}
}
You could write an extension method that would wrap anonymous methods, and even take care of the InvokeRequired semantics:
public static void InvokeAction(this Control ctl, Action a)
{
if (!ctl.InvokeRequired)
{
a();
}
else
{
ctl.BeginInvoke(new MethodInvoker(a));
}
}
This would allow you to do:
control.InvokeAction(delegate() { ConfigureMainMenu(server); });
You can do this in a single method by calling invoking yourself:
ClassData updData = new ClassData();
this.BeginInvoke(new Action<ClassData>(FillCurve),
new object[] { updData });
...
public void FillCurve(ClassData updData)
{
...
}
For completely anonymous methods with a limited number of parameters:
Func<int, int?> caller = new Func<int, int?>((int param1) =>
{
return null;
});
caller.BeginInvoke(7, new AsyncCallback((IAsyncResult ar) =>
{
AsyncResult result = (AsyncResult)ar;
Func<int, int?> action = (Func<int, int?>)result.AsyncDelegate;
action.EndInvoke(ar);
}), null);
You can use one of the other Func delegate types as needed.
I've tried a bunch of different methods but none work. ie...
// Fails -- cannot convert lamda to System.Delegate
mnMnu.BeginInvoke( (DIServer svr)=> {ConfigureMainMenu(server);}, new object[] server);
// Fails -- cannot convert anonymous method to System.Delegate
mnMnu.BeginInvoke( new delegate(DIServer svr){ConfigureMainMenu(server);}, new object[] server);
So, the short answer is no. You could create short helper delegates in the given context and use lambdas to make it a bit neater but that's pretty much it.
EDIT: Turns out I'm wrong. The methodinvoker answer below works.
See this page

Categories

Resources