7

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.

1
  • How are you calling your method? You cannot call it with <A, int>, because T must be a reference type. Commented Jan 12, 2016 at 12:33

1 Answer 1

2

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);
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the answer. I did try your code and it work. But it still doesn't when I using it in my application. I will try to push further before coming back, but still wanted to thank you for bringing hope to me ! Cheers! flo
Ok, I took the time to copy past the full production code in a new Visual project and everything work like a charm. Even in "Monodevelop" it's working. But once I do exactly the same "TestProject" in Unity it crash because "method arguments are incompatible". I will report this to Unity and hope for the best. Any idea about a workaround?
@ThorTillas No idea. Unity uses a rather old version of Mono, so I have no idea what might be wrong, especially since your code jumps through so many hoops.
The Mono bug is still present in Unity. It's been reported in May 2011 here. You can see a Mono unit test file here which references the bug number (695978), but it's tagged as "Not Working". The BugZilla page marks the bug as confirmed, but not fixed. It seems the issue is dead, sadly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.