For serialization purpose, we are trying to generate the delegate to update some object property values on the fly and store them into a list for further use. Everything works quite well as long as we do not try to deserialize structs.
We based our code on this article about open delegate: http://codeblog.jonskeet.uk/2008/08/09/making-reflection-fly-and-exploring-delegates/
And here is our code to deal with property setter in a class based object.
private static System.Action<object, object> ToOpenActionDelegate<T, TParam>(System.Reflection.MethodInfo methodInfo) where T : class
{
System.Type parameterType = typeof(TParam);
// Convert the slow MethodInfo into a fast, strongly typed, open delegate
System.Action<T, TParam> action = (System.Action<T, TParam>)System.Delegate.CreateDelegate(typeof(System.Action<T, TParam>), methodInfo);
// Convert the strong typed delegate into some object delegate!
System.Action<object, object> ret = (object target, object param) => action(target as T, (TParam)System.Convert.ChangeType(param, parameterType));
return ret;
}
As you have guessed, it does not work with struct. I found this post talking about how to deal with open delegate in struct: How can I create an open Delegate from a struct's instance method? (Actually, I found way more post than this one, but this one has a "simple" solution which does not use IL code generation for example...)
But, for now, everytime I try to bind a methodinfo of a property setter to a delegate using a ref parameter, I get an exception. Here is the current code I use:
public delegate void RefAction<T, TParam>(ref T arg, TParam param) where T : class;
private static RefAction<object, object> ToOpenActionDelegate<T, TParam>(System.Reflection.MethodInfo methodInfo) where T : class
{
// Convert the slow MethodInfo into a fast, strongly typed, open delegate
System.Type objectType = typeof(T);
System.Type parameterType = typeof(TParam);
RefAction<object, object> ret;
if (objectType.IsValueType)
{
RefAction<T, TParam> propertySetter = (RefAction<T, TParam>)System.Delegate.CreateDelegate(typeof(RefAction<T, TParam>), methodInfo);
// we are trying to set some struct internal value.
ret = (ref object target, object param) =>
{
T boxed = (T)target;
propertySetter(ref boxed, (TParam)System.Convert.ChangeType(param, parameterType));
target = boxed;
};
}
else
{
System.Action<T, TParam> action = (System.Action<T, TParam>)System.Delegate.CreateDelegate(typeof(System.Action<T, TParam>), methodInfo);
ret = (ref object target, object param) => action(target as T, (TParam)System.Convert.ChangeType(param, parameterType));
}
return ret;
}
The problem appear when executing the following line:
RefAction<T, TParam> propertySetter = (RefAction<T, TParam>)System.Delegate.CreateDelegate(typeof(RefAction<T, TParam>), methodInfo);
Which is, at least for me, really the same as the one used in the linked post above:
SomeMethodHandler d = (SomeMethodHandler)Delegate.CreateDelegate(typeof(SomeMethodHandler), method);
Where:
delegate int SomeMethodHandler(ref A instance);
public struct A
{
private int _Value;
public int Value
{
get { return _Value; }
set { _Value = value; }
}
private int SomeMethod()
{
return _Value;
}
}
Anybody has an idea about why does it generate an exception on my side and not in the linked thread? Is it linked to the C# runtime version? I am working on unity so it's a mono framework almost equivalent to 3.5...
Anyway, thanks for reading and do not hesitate if I am doing something wrong in the question layout or syntaxe!
Cheers, flo.
You're creating a delegate to a static method instead of an open delegate. I added null
to your CreateDelegate
calls and it worked (I am not so sure about the performance, though, with double boxing/unboxing):
public struct S
{
public string Value {get; set;}
}
static class Program
{
public delegate void RefAction<T, TParam>(ref T arg, TParam param);
static RefAction<object, object> ToOpenActionDelegate<T, TParam>(System.Reflection.MethodInfo methodInfo)
{
// Convert the slow MethodInfo into a fast, strongly typed, open delegate
Type objectType = typeof(T);
Type parameterType = typeof(TParam);
RefAction<object, object> ret;
if (objectType.IsValueType)
{
RefAction<T, TParam> propertySetter = (RefAction<T, TParam>)Delegate.CreateDelegate(typeof(RefAction<T, TParam>), null, methodInfo);
// we are trying to set some struct internal value.
ret = (ref object target, object param) =>
{
T boxed = (T)target;
propertySetter(ref boxed, (TParam)System.Convert.ChangeType(param, parameterType));
target = boxed;
};
}
else
{
Action<T, TParam> action = (Action<T, TParam>)Delegate.CreateDelegate(typeof(Action<T, TParam>), null, methodInfo);
ret = (ref object target, object param) => action((T)target, (TParam)System.Convert.ChangeType(param, parameterType));
}
return ret;
}
public static void Main(string[] args)
{
var s = new S();
var mi = s.GetType().GetMethod("set_Value");
/*
var deleg = (RefAction<S, string>)Delegate.CreateDelegate(typeof(RefAction<S, string>), null, mi);
deleg(ref s, "hello");
RefAction<object, object> deleg2 = (ref object target, object param) => {
S boxed = (S)target;
deleg(ref boxed, (string)param);
target = boxed;
};
*/
RefAction<object, object> deleg2 = ToOpenActionDelegate<S, string>(mi);
var o = (object)s;
deleg2(ref o, "world");
s = (S)o;
Console.WriteLine(s.Value); //prints "world"
Console.ReadKey(true);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With