Suppose in C# I have class that has an arbitrary number of Actions, which can have any number of generic arguments:
public class Container
{
public Action a;
public Action<float> b;
public Action<int, float> c;
// etc...
}
And I am registering some debug lambdas on an instance of this class which just print out the name of the action's field:
public static void Main()
{
Container container = new Container();
container.a += () => Console.WriteLine("a was called");
container.b += (temp1) => Console.WriteLine("b was called");
container.c += (temp1, temp2) => Console.WriteLine("c was called");
container.a();
container.b(1.5f);
container.c(1, 1.5f);
}
I would like to automate the creation of these debug lambdas using reflection, as follows:
public static void Main()
{
Container container = new Container();
GenerateDebug(container);
if(container.a != null) container.a();
if(container.b != null) container.b(1.5f);
if(container.c != null) container.c(1, 1.5f);
}
public static void GenerateDebug(Container c)
{
Type t = c.GetType();
FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public);
foreach(FieldInfo field in fields)
{
Action callback = () => Console.WriteLine(field.Name + " was called");
Type[] actionArgTypes = field.FieldType.GetGenericArguments();
if(actionArgTypes.Length == 0)
{
Action action = field.GetValue(c) as System.Action;
action += callback;
field.SetValue(c, action);
}
else
{
// 1. Create an Action<T1, T2, ...> object that takes the types in 'actionArgTypes' which wraps the 'callback' action
// 2. Add this new lambda to the current Action<T1, T2, ...> field
}
}
}
I'm able to get the desired result for Actions with no arguments - the above code does indeed print out "a was called" - but I don't know how to do it for generics.
I believe I know what I need to do, just not how:
Use reflection to create an Action<T1, T2, ...> object using the types in actionArgTypes, which wraps a call to the callback action.
Add this newly created object to the Generic Action specified by the field.
How would I go about doing this, or similar that achieves the desired effect of adding such a debug callback?
Here is a rather simple implementation using Expressions, one could resort to use a ILGenerator directly, but that is not worth the hussle in this case.
public static void GenerateDebug(Container c)
{
Type t = c.GetType();
FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public);
foreach(FieldInfo field in fields)
{
var fieldName = field.Name;
Type[] actionArgTypes = field.FieldType.GetGenericArguments();
// Create paramter expression for each argument
var parameters = actionArgTypes.Select(Expression.Parameter).ToArray();
// Create method call expression with a constant argument
var writeLineCall = Expression.Call(typeof(Console).GetMethod("WriteLine", new [] {typeof(string)}), Expression.Constant(fieldName + " was called"));
// Create and compile lambda using the fields type
var lambda = Expression.Lambda(field.FieldType, writeLineCall, parameters);
var #delegate = lambda.Compile();
var action = field.GetValue(c) as Delegate;
// Combine and set delegates
action = Delegate.Combine(action, #delegate);
field.SetValue(c, action);
}
}
Here is the same function using ILGenerator, that should work with .net framework 2.0+ aswell as .net core. In a real life application there should be checks, caching and probably a whole assemblybuilder:
public static void GenerateDebug(Container c)
{
Type t = c.GetType();
FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public);
foreach(FieldInfo field in fields)
{
var fieldName = field.Name;
Type[] actionArgTypes = field.FieldType.GetGenericArguments();
var dm = new DynamicMethod(fieldName, typeof(void), actionArgTypes);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldstr, fieldName + " was called using ilgen");
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new [] {typeof(string)}));
il.Emit(OpCodes.Ret);
var #delegate = dm.CreateDelegate(field.FieldType);
var action = field.GetValue(c) as Delegate;
// Combine and set delegates
action = Delegate.Combine(action, #delegate);
field.SetValue(c, action);
}
}
Related
I'm trying to use the Convert method on functions as well as actions, so I can avoid writing duplicate methods taking in delegates of Func type. Convert method comes from Convert Action<T> to Action<object>
public class Program
{
static void Main(string[] args)
{
var program = new Program();
var mi = program.GetType().GetMethod("Function", BindingFlags.Instance | BindingFlags.Public);
// Can be any version of Func
var funcType = typeof(Func<int, int>);
// Create action delegate somehow instead
var del = mi.CreateDelegate(funcType, null);
// Or dynamically convert the Func to a corresponding Action type (in this case Action<int>)
}
// Or find a way to pass it in as a parameter here
public Action<object> Convert<T>(Action<T> action)
{
return o => action((T)o);
}
public int Function(int five)
{
return five;
}
}
I think you are looking for something like this:
public static Action<T1> IgnoreResult<T1,T2>(Func<T1,T2> func)
{
return x => func(x);
}
But for all variants of Func<T1,T2....>
I think this would work:
public static Action<TR> IgnoreResult<TR>(Delegate f)
{
return x => f.DynamicInvoke(x);
}
With usage:
var action = IgnoreResult<int>(new Func<int,int>(program.Function));
action(5);
You'll not be able to get it to infer the parameters and return type without copy and pasting the first example for all variants of Action<T1...> and Func<T1,T2...>.
I have the following class hierarchy:
public class Parent
{
[DebuggerStepThrough]
public void SayParent()
{
Console.WriteLine("Parent");
}
}
public sealed class Child : Parent
{
private static int _number = 0;
public Child() // May contain parameter i.e. not always parameterless consctructor
{
_number++;
}
[DebuggerStepThrough]
public void SayInstance()
{
Console.WriteLine("{0}-Say", _number);
}
[DebuggerStepThrough]
public void SayInstanceWithArg(string input)
{
Console.WriteLine("{0}-Say: {1}", _number, input);
}
[DebuggerStepThrough]
public static void SayStatic()
{
Console.WriteLine("{0}-Say", _number);
}
[DebuggerStepThrough]
public static void SayStaticWithArg(string input)
{
Console.WriteLine("{0}-Say: {1}", _number, input);
}
[DebuggerStepThrough]
public static Task SayStaticWithArgAndReturn(string input)
{
Console.WriteLine("{0}-Say: {1}", _number, input);
return null;
}
}
I need to be able to invoke any of these methods for a new instance of Child at any given time using reflection however to improve performance I need to resort to Delegate and/or Compiled Expressions.
So for example I can have:
var instanceOne = new Child();
var instanceTwo = new Child();
for which I would need to at runtime invoke these methods passing the arguments for those that need it. Note they include both static and instance methods with some accepting a parameter.
I have so far tried the following for the "SayInstance" method:
var sayInstanceMethod = typeof(Child)
.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
.Where(m => m.GetCustomAttributes(typeof(DebuggerStepThroughAttribute), true).Length > 0)
.Where(t => t.Name == "SayInstance")
.First()
And then:
var instance = Expression.Constant(new Child()); // This should NOT be Constant, but then what should it be?!
var mCallInstance = Expression.Call(instance, sayInstanceMethod);
Action action = Expression.Lambda<Action>(mCallInstance).Compile();
action();
action(); // I need to pass in a new instance of Child to this method somehow
However I am getting:
1-Say
1-Say
instead of:
1-Say
2-Say
I suspect this is due to Expression.Constant but I cannot figure out how I could let it accept an instance of Child as its target at runtime.
I am hopeless when it comes to Expressions :-(
I am basically trying to implement what Jon Skeet mentions HERE either using Delegates or Compiled Expressions.
Any help is very much appreciated.
If I understood correctly, you need to use parameters, like this:
var instanceOne = new Child();
var instanceTwo = new Child();
var instance = Expression.Parameter(typeof(Child), "c"); // This should NOT be Constant, but then what should it be?!
var mCallInstance = Expression.Call(instance, sayInstanceMethod);
Action<Child> action = Expression.Lambda<Action<Child>>(mCallInstance, instance).Compile();
action(instanceOne);
action(instanceTwo); // I need to pass in a new instance of Child to this method somehow
Of course this will not output 1, 2 because your _number field is static and after creation of two instances has value 2 for both.
EDIT. If you need to call method with arguments - declare more parameters. For example if SayInstance has one argument of type string, then:
var instanceOne = new Child();
var instanceTwo = new Child();
var instance = Expression.Parameter(typeof(Child), "instance");
var arg = Expression.Parameter(typeof(string), "arg");
var mCallInstance = Expression.Call(instance, sayInstanceMethod, arg);
Action<Child,string> action = Expression.Lambda<Action<Child,string>>(mCallInstance, instance, arg).Compile();
action(instanceOne, "one");
action(instanceTwo, "two");
Try this out, this workf for me for a parameterless constructor, but this is that you need:
var instance = Expression.New(typeof(Child).GetConstructor(new Type[0]));
var mCallInstance = Expression.Call(instance, sayInstanceMethod);
Action action = Expression.Lambda<Action>(mCallInstance).Compile();
action();
action(); // I need to pass in a new instance of Child to this method someh
I'm struggling to create instances of Action<> using the MethodInfo's I can retrieve using reflection.
Using the example below I can easily call a custom method on my objects, but I would really like to convert them to Actions.
I have tried doing it by using Delegate.CreateDelegate but I can't seem to get this to return an Action with a generic type.
Example
I have created the following interface:
interface IMyTest { } // Marker interface
interface<TInput> IMyTest : IMyTest {
void MyMethod(TInput input);
}
Then I inherit the interface in a class:
class MyClass : IMyTest<DateTime>, IMyTest<int> {
void MyMethod(DateTime input) {
// Do something
}
void MyMethod(int input) {
// Do something else
}
}
And then I create a method that uses reflection to find and call "MyMethod" from the interface instance:
void DoCallMyMethod(object target, object input) {
IEnumerable<Type> interfaces = target.GetType().GetInterfaces()
.Where(x => typeof(IMyTest).IsAssignableFrom(x) && x.IsGenericType);
foreach (Type #interface in interfaces) {
Type type = #interface.GetGenericArguments()[0];
MethodInfo method = #interface.GetMethod("MyMethod", new Type[] { type });
if (method != null) {
method.Invoke(target, new[] { input });
}
}
}
And finally I put it all together:
MyClass foo = new MyClass();
DoCallMyMethod(foo, DateTime.Now);
DoCallMyMethod(foo, 47);
What I would want
Inside DoCallMyMethod I would like MethodInfo method to be converted to a generic Action so that the result would be something like this:
Action<type> myAction = method;
But this obviously doesn't work.
I found some similar SO posts (but none that cover exactly my case) that ended up with an answer similar to this:
Action<object> action =
(Action<object>)Delegate.CreateDelegate(typeof(Action<object>), target, method);
But this doesn't work because "Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type."
How can I get an Action with the specified type as its input type, while still retaining the object references (without new instances appearing)?
You can't create an Action<object> from one of MyMethods because the method is expecting a specific object's type (int, DateTime) and if you treat it as an Action<object> you might invoke it with objects of any type.
Since what you need is return a Action<T> there is not need to pass the input value to DoCallMyMethod method. You can pass the input type as a generic parameter:
public Action<T> DoCallMyMethod<T>(object target)
{
var #interface = typeof(IMyTest<T>);
if (#interface.IsAssignableFrom(target.GetType()))
{
var method = #interface.GetMethod("MyMethod");
if (method != null)
{
var action = Delegate.CreateDelegate(typeof(Action<T>), target, method) as Action<T>;
return action;
}
}
return null;
}
Then, use it like this:
MyClass foo = new MyClass();
var action1 = DoCallMyMethod<DateTime>(foo);
var action2 = DoCallMyMethod<int>(foo);
action1(DateTime.Now);
action2(47);
If you don't know at compile time the type of the input you can try this:
public Delegate DoCallMyMethod(object target, Type inputType)
{
var #interface = typeof(IMyTest<>).MakeGenericType(inputType);
if (#interface.IsAssignableFrom(target.GetType()))
{
var method = #interface.GetMethod("MyMethod");
if (method != null)
{
var #delegate = Delegate.CreateDelegate(typeof(Action<>).MakeGenericType(inputType), target, method);
return #delegate;
}
}
return null;
}
And use it like so:
MyClass foo = new MyClass();
var action1 = DoCallMyMethod(foo, typeof(DateTime)) as Action<DateTime>;
var action2 = DoCallMyMethod(foo, typeof(int)) as Action<int>;
action1(DateTime.Now);
action2(47);
//or
int input = 47;
var #delegate = DoCallMyMethod(foo, input.GetType());
#delegate.DynamicInvoke(input);
But if you need to returns an Action<object> you can make an action receiving an object and calling the method if the object's type is valid:
public Action<object> DoCallMyMethod(object target, Type inputType)
{
var #interface = typeof(IMyTest<>).MakeGenericType(inputType);
if (#interface.IsAssignableFrom(target.GetType()))
{
var method = #interface.GetMethod("MyMethod");
if (method != null)
{
Action<object> action = obj =>
{
if (obj.GetType().IsAssignableFrom(inputType))
method.Invoke(target, new object[] { obj });
};
return action;
}
}
return null;
}
And...
MyClass foo = new MyClass();
Action<object> action = DoCallMyMethod(foo, typeof(int));
action(47); // MyMethod(int) is called.
action("..."); // Nothing happens.
Now you have an Action<object> that calls MyMethod(int) only when an integer is passed.
Using the new async/await model it's fairly straightforward to generate a Task that is completed when an event fires; you just need to follow this pattern:
public class MyClass
{
public event Action OnCompletion;
}
public static Task FromEvent(MyClass obj)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
obj.OnCompletion += () =>
{
tcs.SetResult(null);
};
return tcs.Task;
}
This then allows:
await FromEvent(new MyClass());
The problem is that you need to create a new FromEvent method for every event in every class that you would like to await on. That could get really large really quick, and it's mostly just boilerplate code anyway.
Ideally I would like to be able to do something like this:
await FromEvent(new MyClass().OnCompletion);
Then I could re-use the same FromEvent method for any event on any instance. I've spent some time trying to create such a method, and there are a number of snags. For the code above it will generate the following error:
The event 'Namespace.MyClass.OnCompletion' can only appear on the left hand side of += or -=
As far as I can tell, there won't ever be a way of passing the event like this through code.
So, the next best thing seemed to be trying to pass the event name as a string:
await FromEvent(new MyClass(), "OnCompletion");
It's not as ideal; you don't get intellisense and would get a runtime error if the event doesn't exist for that type, but it could still be more useful than tons of FromEvent methods.
So it's easy enough to use reflection and GetEvent(eventName) to get the EventInfo object. The next problem is that the delegate of that event isn't known (and needs to be able to vary) at runtime. That makes adding an event handler hard, because we need to dynamically create a method at runtime, matching a given signature (but ignoring all parameters) that accesses a TaskCompletionSource that we already have and sets its result.
Fortunately I found this link which contains instructions on how to do [almost] exactly that via Reflection.Emit. Now the problem is that we need to emit IL, and I have no idea how to access the tcs instance that I have.
Below is the progress that I've made towards finishing this:
public static Task FromEvent<T>(this T obj, string eventName)
{
var tcs = new TaskCompletionSource<object>();
var eventInfo = obj.GetType().GetEvent(eventName);
Type eventDelegate = eventInfo.EventHandlerType;
Type[] parameterTypes = GetDelegateParameterTypes(eventDelegate);
DynamicMethod handler = new DynamicMethod("unnamed", null, parameterTypes);
ILGenerator ilgen = handler.GetILGenerator();
//TODO ilgen.Emit calls go here
Delegate dEmitted = handler.CreateDelegate(eventDelegate);
eventInfo.AddEventHandler(obj, dEmitted);
return tcs.Task;
}
What IL could I possibly emit that would allow me to set the result of the TaskCompletionSource? Or, alternatively, is there another approach to creating a method that returns a Task for any arbitrary event from an arbitrary type?
Here you go:
internal class TaskCompletionSourceHolder
{
private readonly TaskCompletionSource<object[]> m_tcs;
internal object Target { get; set; }
internal EventInfo EventInfo { get; set; }
internal Delegate Delegate { get; set; }
internal TaskCompletionSourceHolder(TaskCompletionSource<object[]> tsc)
{
m_tcs = tsc;
}
private void SetResult(params object[] args)
{
// this method will be called from emitted IL
// so we can set result here, unsubscribe from the event
// or do whatever we want.
// object[] args will contain arguments
// passed to the event handler
m_tcs.SetResult(args);
EventInfo.RemoveEventHandler(Target, Delegate);
}
}
public static class ExtensionMethods
{
private static Dictionary<Type, DynamicMethod> s_emittedHandlers =
new Dictionary<Type, DynamicMethod>();
private static void GetDelegateParameterAndReturnTypes(Type delegateType,
out List<Type> parameterTypes, out Type returnType)
{
if (delegateType.BaseType != typeof(MulticastDelegate))
throw new ArgumentException("delegateType is not a delegate");
MethodInfo invoke = delegateType.GetMethod("Invoke");
if (invoke == null)
throw new ArgumentException("delegateType is not a delegate.");
ParameterInfo[] parameters = invoke.GetParameters();
parameterTypes = new List<Type>(parameters.Length);
for (int i = 0; i < parameters.Length; i++)
parameterTypes.Add(parameters[i].ParameterType);
returnType = invoke.ReturnType;
}
public static Task<object[]> FromEvent<T>(this T obj, string eventName)
{
var tcs = new TaskCompletionSource<object[]>();
var tcsh = new TaskCompletionSourceHolder(tcs);
EventInfo eventInfo = obj.GetType().GetEvent(eventName);
Type eventDelegateType = eventInfo.EventHandlerType;
DynamicMethod handler;
if (!s_emittedHandlers.TryGetValue(eventDelegateType, out handler))
{
Type returnType;
List<Type> parameterTypes;
GetDelegateParameterAndReturnTypes(eventDelegateType,
out parameterTypes, out returnType);
if (returnType != typeof(void))
throw new NotSupportedException();
Type tcshType = tcsh.GetType();
MethodInfo setResultMethodInfo = tcshType.GetMethod(
"SetResult", BindingFlags.NonPublic | BindingFlags.Instance);
// I'm going to create an instance-like method
// so, first argument must an instance itself
// i.e. TaskCompletionSourceHolder *this*
parameterTypes.Insert(0, tcshType);
Type[] parameterTypesAr = parameterTypes.ToArray();
handler = new DynamicMethod("unnamed",
returnType, parameterTypesAr, tcshType);
ILGenerator ilgen = handler.GetILGenerator();
// declare local variable of type object[]
LocalBuilder arr = ilgen.DeclareLocal(typeof(object[]));
// push array's size onto the stack
ilgen.Emit(OpCodes.Ldc_I4, parameterTypesAr.Length - 1);
// create an object array of the given size
ilgen.Emit(OpCodes.Newarr, typeof(object));
// and store it in the local variable
ilgen.Emit(OpCodes.Stloc, arr);
// iterate thru all arguments except the zero one (i.e. *this*)
// and store them to the array
for (int i = 1; i < parameterTypesAr.Length; i++)
{
// push the array onto the stack
ilgen.Emit(OpCodes.Ldloc, arr);
// push the argument's index onto the stack
ilgen.Emit(OpCodes.Ldc_I4, i - 1);
// push the argument onto the stack
ilgen.Emit(OpCodes.Ldarg, i);
// check if it is of a value type
// and perform boxing if necessary
if (parameterTypesAr[i].IsValueType)
ilgen.Emit(OpCodes.Box, parameterTypesAr[i]);
// store the value to the argument's array
ilgen.Emit(OpCodes.Stelem, typeof(object));
}
// load zero-argument (i.e. *this*) onto the stack
ilgen.Emit(OpCodes.Ldarg_0);
// load the array onto the stack
ilgen.Emit(OpCodes.Ldloc, arr);
// call this.SetResult(arr);
ilgen.Emit(OpCodes.Call, setResultMethodInfo);
// and return
ilgen.Emit(OpCodes.Ret);
s_emittedHandlers.Add(eventDelegateType, handler);
}
Delegate dEmitted = handler.CreateDelegate(eventDelegateType, tcsh);
tcsh.Target = obj;
tcsh.EventInfo = eventInfo;
tcsh.Delegate = dEmitted;
eventInfo.AddEventHandler(obj, dEmitted);
return tcs.Task;
}
}
This code will work for almost all events that return void (regardless of the parameter list).
It can be improved to support any return values if necessary.
You can see the difference between Dax's and mine methods below:
static async void Run() {
object[] result = await new MyClass().FromEvent("Fired");
Console.WriteLine(string.Join(", ", result.Select(arg =>
arg.ToString()).ToArray())); // 123, abcd
}
public class MyClass {
public delegate void TwoThings(int x, string y);
public MyClass() {
new Thread(() => {
Thread.Sleep(1000);
Fired(123, "abcd");
}).Start();
}
public event TwoThings Fired;
}
Briefly, my code supports really any kind of delegate type. You shouldn't (and don't need to) specify it explicitly like TaskFromEvent<int, string>.
This will give you what you need without needing to do any ilgen, and way simpler. It works with any kind of event delegates; you just have to create a different handler for each number of parameters in your event delegate. Below are the handlers you'd need for 0..2, which should be the vast majority of your use cases. Extending to 3 and above is a simple copy and paste from the 2-parameter method.
This is also more powerful than the ilgen method because you can use any values created by the event in your async pattern.
// Empty events (Action style)
static Task TaskFromEvent(object target, string eventName) {
var addMethod = target.GetType().GetEvent(eventName).GetAddMethod();
var delegateType = addMethod.GetParameters()[0].ParameterType;
var tcs = new TaskCompletionSource<object>();
var resultSetter = (Action)(() => tcs.SetResult(null));
var d = Delegate.CreateDelegate(delegateType, resultSetter, "Invoke");
addMethod.Invoke(target, new object[] { d });
return tcs.Task;
}
// One-value events (Action<T> style)
static Task<T> TaskFromEvent<T>(object target, string eventName) {
var addMethod = target.GetType().GetEvent(eventName).GetAddMethod();
var delegateType = addMethod.GetParameters()[0].ParameterType;
var tcs = new TaskCompletionSource<T>();
var resultSetter = (Action<T>)tcs.SetResult;
var d = Delegate.CreateDelegate(delegateType, resultSetter, "Invoke");
addMethod.Invoke(target, new object[] { d });
return tcs.Task;
}
// Two-value events (Action<T1, T2> or EventHandler style)
static Task<Tuple<T1, T2>> TaskFromEvent<T1, T2>(object target, string eventName) {
var addMethod = target.GetType().GetEvent(eventName).GetAddMethod();
var delegateType = addMethod.GetParameters()[0].ParameterType;
var tcs = new TaskCompletionSource<Tuple<T1, T2>>();
var resultSetter = (Action<T1, T2>)((t1, t2) => tcs.SetResult(Tuple.Create(t1, t2)));
var d = Delegate.CreateDelegate(delegateType, resultSetter, "Invoke");
addMethod.Invoke(target, new object[] { d });
return tcs.Task;
}
Use would be like this. As you can see, even though the event is defined in a custom delegate, it still works. And you can capture the evented values as a tuple.
static async void Run() {
var result = await TaskFromEvent<int, string>(new MyClass(), "Fired");
Console.WriteLine(result); // (123, "abcd")
}
public class MyClass {
public delegate void TwoThings(int x, string y);
public MyClass() {
new Thread(() => {
Thread.Sleep(1000);
Fired(123, "abcd");
}).Start();
}
public event TwoThings Fired;
}
Here's a helper function that'll allow you to write the TaskFromEvent functions in just one line each, if the above three methods are too much copy-and-paste for your preferences. Credit has to be given to max for simplifying what I had originally.
If you're willing to have one method per delegate type, you can do something like:
Task FromEvent(Action<Action> add)
{
var tcs = new TaskCompletionSource<bool>();
add(() => tcs.SetResult(true));
return tcs.Task;
}
You would use it like:
await FromEvent(x => new MyClass().OnCompletion += x);
Be aware that this way you never unsubscribe from the event, that may or may not be a problem for you.
If you're using generic delegates, one method per each generic type is enough, you don't need one for each concrete type:
Task<T> FromEvent<T>(Action<Action<T>> add)
{
var tcs = new TaskCompletionSource<T>();
add(x => tcs.SetResult(x));
return tcs.Task;
}
Although type inference doesn't work with that, you have to explicitly specify the type parameter (assuming the type of OnCompletion is Action<string> here):
string s = await FromEvent<string>(x => c.OnCompletion += x);
I faced this problem by trying to write GetAwaiter extension method for System.Action, forgetting that System.Action is immutable and by passing it as an argument you make a copy. However, you do not make a copy if you pass it with ref keyword, thus:
public static class AwaitExtensions
{
public static Task FromEvent(ref Action action)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
action += () => tcs.SetResult(null);
return tcs.Task;
}
}
Usage:
await AwaitExtensions.FromEvent(ref OnActionFinished);
Note: TCS listener remain subscribed
I want to take a class, loop through it's properties, get the property value, and call a method passing that property value in. I think I can get the property values, but what does the lambda expression's body look like? What body is used to call a method on each property?
This is what I have so far...
Action<T> CreateExpression<T>( T obj )
{
foreach( var property in typeof( T ).GetProperties() )
{
Expression value = Expression.Property( Expression.Constant( obj ), property );
var method = Expression.Call( typeof( SomeType ), "SomeMethod", null, value );
}
// What expression body can be used that will call
// all the method expressions for each property?
var body = Expression...
return Expression.Lambda<Action<T>>( body, ... ).Compile();
}
It depends on a few things.
does the method return anything? Expression in 3.5 can't do multiple separate "action" operations (a statement body), but you can cheat if you can do something with a fluent API:
SomeMethod(obj.Prop1).SomeMethod(obj.Prop2).SomeMethod(obj.Prop3);
(perhaps using generics to make it simpler)
do you have access to 4.0? In 4.0 there are additional Expression types allowing statement bodies and exactly what you ask for. I discuss some similar examples in an article here (look for Expression.Block, although this is based on a beta a while ago - it may have been renamed by now).
Alternative; since you are compiling to a delegate, consider that an Action<T> is multicast; you could build a set of simple operations, and combine them in the delegate; this would work in 3.5; for example:
using System;
using System.Linq.Expressions;
static class SomeType
{
static void SomeMethod<T>(T value)
{
Console.WriteLine(value);
}
}
class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
static class Program
{
static readonly Action<Customer> action = CreateAction<Customer>();
static void Main()
{
Customer cust = new Customer { Id = 123, Name = "Abc" };
action(cust);
}
static Action<T> CreateAction<T>()
{
Action<T> result = null;
var param = Expression.Parameter(typeof(T), "obj");
foreach (var property in typeof(T).GetProperties(
BindingFlags.Instance | BindingFlags.Public))
{
if (property.GetIndexParameters().Length > 0) continue;
var propVal = Expression.Property(param, property);
var call = Expression.Call(typeof(SomeType), "SomeMethod", new Type[] {propVal.Type}, propVal);
result += Expression.Lambda<Action<T>>(call, param).Compile();
}
return result;
}
}
I dont think it will be so easy using Expressions, in .NET 3.5 at least.
.NET 4 supports a block construct I believe.
I suggest using Reflection.Emit rather.
Here is a starting point (for fields but can be changed easily):
internal static T CreateDelegate<T>(this DynamicMethod dm) where T : class
{
return dm.CreateDelegate(typeof(T)) as T;
}
static Dictionary<Type, Func<object, Dictionary<string, object>>> fieldcache =
new Dictionary<Type, Func<object, Dictionary<string, object>>>();
static Dictionary<string, object> GetFields(object o)
{
var t = o.GetType();
Func<object, Dictionary<string, object>> getter;
if (!fieldcache.TryGetValue(t, out getter))
{
var rettype = typeof(Dictionary<string, object>);
var dm = new DynamicMethod(t.Name + ":GetFields",
rettype, new Type[] { typeof(object) }, t);
var ilgen = dm.GetILGenerator();
var instance = ilgen.DeclareLocal(t);
var dict = ilgen.DeclareLocal(rettype);
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Castclass, t);
ilgen.Emit(OpCodes.Stloc, instance);
ilgen.Emit(OpCodes.Newobj, rettype.GetConstructor(Type.EmptyTypes));
ilgen.Emit(OpCodes.Stloc, dict);
var add = rettype.GetMethod("Add");
foreach (var field in t.GetFields(
BindingFlags.DeclaredOnly |
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic))
{
if (!field.FieldType.IsSubclassOf(typeof(Component)))
{
continue;
}
ilgen.Emit(OpCodes.Ldloc, dict);
ilgen.Emit(OpCodes.Ldstr, field.Name);
ilgen.Emit(OpCodes.Ldloc, instance);
ilgen.Emit(OpCodes.Ldfld, field);
ilgen.Emit(OpCodes.Castclass, typeof(object));
ilgen.Emit(OpCodes.Callvirt, add);
}
ilgen.Emit(OpCodes.Ldloc, dict);
ilgen.Emit(OpCodes.Ret);
fieldcache[t] = getter = dm.CreateDelegate<Func<object,
Dictionary<string, object>>>();
}
return getter(o);
}
Use the Block statement. The code below for example writes out the names of all properties
static void WritePropertyNames()
{
TestObject lTestObject = new TestObject();
PropertyInfo[] lProperty = typeof(TestObject).GetProperties();
List<Expression> lExpressions = new List<Expression>();
MethodInfo lMethodInfo = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) });
lProperty.ForEach(x =>
{
ConstantExpression lConstant = Expression.Constant(x.Name);
MethodCallExpression lMethodCall = Expression.Call(lMethodInfo, lConstant);
lExpressions.Add(lMethodCall);
});
BlockExpression lBlock = Expression.Block(lExpressions);
LambdaExpression lLambda = Expression.Lambda<Action>(lBlock, null);
Action lWriteProperties = lLambda.Compile() as Action;
lWriteProperties();
}
Expression trees can only contain a single statement. To do what you are trying you would need to Expression.Lambda<>() in your loop, passing "method" as the body.
I believe this has changed in .NET Framework 4.0.
Andrew
If you're willing to have your method SomeType.SomeMethod accept an object[] then you can do something like this (note that indexers can not be handled here so we discard them):
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Test {
class SomeType {
public static void SomeMethod(object[] values) {
foreach (var value in values) {
Console.WriteLine(value);
}
}
}
class Program {
static Action<T> CreateAction<T>() {
ParameterExpression parameter = Expression.Parameter(
typeof(T),
"parameter"
);
List<Expression> properties = new List<Expression>();
foreach (var info in typeof(T).GetProperties()) {
// can not handle indexers
if(info.GetIndexParameters().Length == 0) {
Expression property = Expression.Property(parameter, info);
properties.Add(Expression.Convert(property, typeof(object)));
}
}
Expression call = Expression.Call(
typeof(SomeType).GetMethod("SomeMethod"),
Expression.NewArrayInit(typeof(object), properties)
);
return Expression.Lambda<Action<T>>(call, parameter).Compile();
}
static void Main(string[] args) {
Customer c = new Customer();
c.Name = "Alice";
c.ID = 1;
CreateAction<Customer>()(c);
}
}
class Customer {
public string Name { get; set; }
public int ID { get; set; }
}
}
Of course this will be easier in .NET 4.0 with the LoopExpression.