i am attempting to attach a Delegate to an invocation list of a different delegate.
By that i am achieving a kind of Hook on existing events.
I need to hook up something that runs after each event that is invoked.
The following example works as long as the Delegate exposed by the type and the Action i pass in have the exact same signature.
(On1 and OnAll events are both declared with an Action delegate so it works).
Code : How i hook up an Action with an existing delegate exposed by an event modifier.
public static class ReflectionExtensions
{
public static IEnumerable<EventInfo> GetEvents(this object obj)
{
var events = obj.GetType().GetEvents();
return events;
}
public static void AddHandler(this object obj, Action action)
{
var events = obj.GetEvents();
foreach (var #event in events)
{
#event.AddEventHandler(obj, action);
}
}
}
The Sample :
public class Tester
{
public event Action On1;
public event Action On2;
public void RaiseOn1()
{
On1();
}
public void RaiseOn2()
{
On2();
}
}
class Program
{
static void Main(string[] args)
{
var t = new Tester();
t.On1 += On1;
t.On2 += On2;
t.AddHandler(OnAll);
t.RaiseOn1();
t.RaiseOn2();
}
public void On1() { }
public void On2() { }
public void OnAll() { }
}
The Problem : When the Delegate exposed with an event modifier in Tester does not have the same signature i get a well wanted and obvious exception which states (in my words) that Action can't be added to an invocation list of an Action<int> . makes sense.
Just to be clear I'm describing the following :
public event Action<int> On1;
public void On1(int i){}
What I'm looking for is a way to create another Delegate of the same type as the EventHandlerType. In order to do that i need to create a method with the signature i of EventHandlerType which would internally invoke action.
something like :
public static void AddHandler(this object obj, Action action)
{
var events = obj.GetEvents();
foreach (var #event in events)
{
// method with the signeture of EventHandlerType which does action();
MethodInfo wrapperMethod = WrapAction(#event.EventHandlerType, action);
Delegate handler = Delegate.CreateDelegate(#event.EventHandlerType, action.Target, wrapperMethod);
#event.AddEventHandler(obj, handler);
}
}
This seems to work... There are various comments inside... I'm not sure if this is the best way to do it. I'm building an Expression tree to do the delegate invocation.
public static void AddHandler(this object obj, Action action)
{
var events = obj.GetEvents();
foreach (var #event in events)
{
// Simple case
if (#event.EventHandlerType == typeof(Action))
{
#event.AddEventHandler(obj, action);
}
else
{
// From here: http://stackoverflow.com/a/429564/613130
// We retrieve the parameter types of the event handler
var parameters = #event.EventHandlerType.GetMethod("Invoke").GetParameters();
// We convert it to ParameterExpression[]
ParameterExpression[] parameters2 = Array.ConvertAll(parameters, x => Expression.Parameter(x.ParameterType));
MethodCallExpression call;
// Note that we are "opening" the delegate and using
// directly the Target and the Method! Inside the
// LambdaExpression we will build there won't be a
// delegate call, there will be a method call!
if (action.Target == null)
{
// static case
call = Expression.Call(action.Method);
}
else
{
// instance type
call = Expression.Call(Expression.Constant(action.Target), action.Method);
}
// If you are OK to create a delegate that calls another
// delegate, you can:
// call = Expression.Call(Expression.Constant(action), typeof(Action).GetMethod("Invoke"));
// instead of the big if/else
var lambda = Expression.Lambda(#event.EventHandlerType, call, parameters2);
#event.AddEventHandler(obj, lambda.Compile());
}
}
}
Related
I am working on implementing pub/sub pattern for cache use. The following class is suppose to be used to publish any type of data. And subscribers can attach a callback of Action T on the event. But I couldn't create a generic event at the class level without making the class generic and I don't want to do that. So I have tried to cast the values back and forth to string using JsonSerializer as follows.
public class CacheNotification
{
public Action<string> CachePublished;
public Task Publish<T>(T value)
{
var json = JsonSerializer.Serialize(value);
OnCachePublished(json);
return Task.CompletedTask;
}
public Task Subscribe<T>(Action<T> callback)
{
Action<string> newCallback = (string json) =>
callback(JsonSerializer.Deserialize<T>(json));
CachePublished += newCallback;
return Task.CompletedTask;
}
protected virtual void OnCachePublished(string value)
{
if(CachePublished != null){
CachePublished.Invoke(value);
}
}
}
So the problem is on the subscribe method, the casting I attempted only works for Action string. It fails on Action int or Action object
public Task Subscribe<T>(Action<T> callback)
{
Action<string> newCallback = (string json) =>
callback(JsonSerializer.Deserialize<T>(json));
CachePublished += newCallback;
return Task.CompletedTask;
}
Here is a test I used. This fails for second subscriber
Action<string> sub1Callback = msg =>
{
sub1Counter++;
};
Action<int> sub2Callback = num =>
{
sub2Counter++;
};
var sub1 = await cache.Subscribe(sub1Callback);
var sub2 = await cache.Subscribe(sub2Callback);
await cache.Publish("Value1");
Assert.Equal(1, sub1Counter);
Assert.Equal(1, sub2Counter);
your problem is here:
Action sub2Callback = num =>
after
await cache.Publish("Value1");
Your:
public Task Subscribe<T>(Action<T> callback)
{
Action<string> newCallback = (string json) => callback(JsonSerializer.Deserialize<T>(json));
CachePublished += newCallback;
return Task.CompletedTask;
}
tries to deserialize "Value1" to integer and it correctly fails. It cannot be done.
Try to send integer value instead of "Value1" and it should work.
Another way is to refactor your Subscribe method to be independent of given type, but this cannot be done according to your intentions I guess.
I changed MyAction after I pass MyAction to TestMethod, when I call t.Next(3) I hope the changed MyAction method can be called, I cannot pass MyAction as ref or out, because ref or out are not supported in an anonymous function, is there anyway can archive this purpose?
I know I can change the sequence, first set MyAction to a method and then call t.TestMethod(MyAction), but the requirement in the real world is I have to change MyAction after I called t.TestMethod(MyAction)
class Program
{
static void Main(string[] args)
{
Test t = new Test();
t.TestMethod(MyAction);
MyAction = new Action(() => { Console.WriteLine(1); });
t.Next(3);
}
public static Action MyAction;
}
class Test
{
Subject<int> sub = new Subject<int>();
public void Next(int v)
{
sub.OnNext(v);
}
public void TestMethod(Action action)
{
Enumerable.Range(1, 2).ToObservable().Concat(sub).Subscribe(p =>
{
if (action != null) action();
});
}
}
Instead of passing Action action, you could pass a Func<Action> actionProvider as a parameter, and then dispatch it like this:
actionProvider()();
I would like to introduce a method/function which can receive an Action or a Func<T>, and depending on what it got, it should return void or T.
Now I have to write this method in two, almost same versions like this.
public void WrapAction(Action action) {
// do something...
action();
// do something more...
}
public T WrapFunc(Func<T> func) {
// do something...
var result = func();
// do something more...
return result;
}
Is there any technique to avoid this repetition?
In contrast, for example in Javascript I have the flexibility to accept any kind of a function (may be void) and just return it's result, and if it was void, then it will return undefined. But in C# one cannot write something like Func<void>.
What about to make an Func<bool> out of an action via an extension method and reduce the wrapper to handle Func<T> only.
public static class ActionExtensions
{
public static Func<bool> ToFunc(this Action action)
{
return () =>
{
action();
return true;
};
}
}
Then WrapAction could simply call WrapFunc.
public void WrapAction(Action action)
{
WrapFunc(action.ToFunc());
}
Or remove WrapAction at all and use WrapFunc directly.
WrapFunc(action.ToFunc());
One possible solution is that you can extract the repeating bits out to separate methods in the same class so, something like this...
public void WrapAction(Action action) {
DoInitialBit();
action();
DoFinalBit();
}
public T WrapFunc(Func<T> func) {
DoInitialBit();
var result = func();
DoFinalBit();
return result;
}
private void DoInitialBit()
{
// Do the thing before you call the Action or Func
}
private void DoFinalBit()
{
// Do the thing after you call the Action or Func
}
Obviously, you may have to take inputs or return outputs from these additional methods as required but that's the general gist of it.
Some really ugly code to have code DRY:
static void Main(string[] args)
{
var p = new Program();
p.WrapAction(() => Console.WriteLine(123));
Console.WriteLine(p.WrapFunc<string>(() => "321"));
Console.ReadKey();
}
public void WrapAction(Action action) => WrapActionInner<object>(action);
public T WrapFunc<T>(Func<T> func) => WrapActionInner<T>(func);
private T WrapActionInner<T>(object action)
{
if (action is Action)
{
((Action)action)();
return default(T);
}
return ((Func<T>)action)();
}
The idea is to wrap functionality into type unsafe private method.
I have my MainClass and MethodFinder classes. I want to obtain the method name to be run in Main via MethodFinder and execute it.
How can I do that?
What I want is a class with a method that returns Method1 or Method2 (based on some criteria) which then can be run in MainClass's MainMethod!
public MainClass
{
public void Main()
{
var methodFinder = new MethodFinder();
var method = methodFinder.Find();
// Execute method
}
private void Method1(){}
private void Method2(){}
}
You can use the Action type in this situation, as long as the methods have the same signature. If your method took parameters, you could use Action<T>. If it returned a value, you could use Func<TResult>.
public Action Find(SomeType someParameter)
{
if (someCondition)
{
return new Action(() => Method1());
}
else
{
return new Action(() => Method2());
}
}
Note though this method smells like a case where you might want to use Polymorphism to achieve your goals, or possibly Dependency Injection.
You could do this, or use Reflection to be more dynamic.
public class MethodFinder
{
public delegate void MethodSignature();
//these can live whereever and even be passed in
private static void Method1() => Debug.WriteLine("Method1 executed");
private static void Method2() => Debug.WriteLine("Method2 executed");
//maintain an array of possibilities or soemthing.
//perhaps use reflection instead
private MethodSignature[] methods = new MethodSignature[] { Method1, Method2 };
public MethodSignature FindByName(string methodName)
=> (from m in methods
where m.Method.Name == methodName
select m).FirstOrDefault();
}
Usage:
var methodFinder = new MethodFinder();
var method = methodFinder.FindByName("Method2");
method(); //output: "Method2 executed"
I want to get the object associated with an Action delegate and determine whether its null. How would I go about this? and is it possible without reflection?
Action is a delegate type and therefore it's base class is MulticastDelegate inherited from Delegate which exposes Method and Target properties. You are probably interested in Target as it "Gets the class instance on which the current delegate invokes the instance method." It's value is "The object on which the current delegate invokes the instance method, if the delegate represents an instance method; null if the delegate represents a static method."
If you cast you Action object to MulticastDelegate (or Delegate) you can verify that like in this code snippet:
public class A
{
public void foo()
{
Console.WriteLine("A.foo()");
}
public void foo2()
{
Console.WriteLine("A.foo2()");
}
public static void bar()
{
Console.WriteLine("A.bar()");
}
}
class Program
{
static void Main(string[] args)
{
A a = new A();
Action action = a.foo;
action += a.foo2;
MulticastDelegate d = (MulticastDelegate)action;
Debug.Assert(object.ReferenceEquals(d.Target, a)); // passes
action();
action = A.bar;
d = (MulticastDelegate)action;
Debug.Assert(object.ReferenceEquals(d.Target, null)); // passes
action();
}
}
Output:
A.foo()
A.foo2()
A.bar()
Note that Target returns the instance of the last object added to the invocation list:
public class B
{
public void foo()
{
Console.WriteLine("B.foo()");
}
}
class Program
{
static void Main(string[] args)
{
A a = new A();
B b = new B();
Action action = a.foo;
action += a.foo2;
action += b.foo;
MulticastDelegate d = (MulticastDelegate)action;
Debug.Assert(object.ReferenceEquals(d.Target, b)); // passes
action();
action = A.bar;
d = (MulticastDelegate)action;
Debug.Assert(object.ReferenceEquals(d.Target, null)); // passes
action();
}
}
Output:
A.foo()
A.foo2()
B.foo()
A.bar()