How can I create an open Delegate from a struct's instance method?

后端 未结 3 1680
鱼传尺愫
鱼传尺愫 2020-12-03 23:44

I have a struct with a private method that I\'d like to invoke. Since I plan to do this in a performance critical section, I\'d like to cache a delegate to perform the actio

相关标签:
3条回答
  • 2020-12-03 23:55

    The first parameter of an unbound instance method delegate cannot be a value type. This is because of how value types must be handled when used as 'this' parameters. You can't simply pass them by value (as would occur if you were passing a value type as the first parameter of a static method) as then the method is acting on a copy and any mutation of the copy has no effect on the original.


    UPDATE: As noted in another answer, value types when used as 'this' parameters are effectively passed by reference.

    0 讨论(0)
  • 2020-12-03 23:56

    Interesting problem. From this bug report, it looks like this might be a bug that will be fixed in a future version of .NET: http://connect.microsoft.com/VisualStudio/feedback/details/574959/cannot-create-open-instance-delegate-for-value-types-methods-which-implement-an-interface#details

    EDIT: actually, I think this bug report is regarding a different issue, so the behavior you're seeing may not actually be a bug.

    From that bug report, I gleaned that there is a work-around if you specify the first argument of your delegate as being passed by reference. Below is a complete working example:

    public struct A
    {
        private int _Value;
    
        public int Value
        {
            get { return _Value; }
            set { _Value = value; }
        }
    
        private int SomeMethod()
        {
            return _Value;
        }
    }
    
    delegate int SomeMethodHandler(ref A instance);
    
    class Program
    {
        static void Main(string[] args)
        {
            var method = typeof(A).GetMethod("SomeMethod", BindingFlags.Instance | BindingFlags.NonPublic);
    
            SomeMethodHandler d = (SomeMethodHandler)Delegate.CreateDelegate(typeof(SomeMethodHandler), method);
    
            A instance = new A();
    
            instance.Value = 5;
    
            Console.WriteLine(d(ref instance));
        }
    }
    

    EDIT: Jon Skeet's answer here also discusses this issue.

    0 讨论(0)
  • 2020-12-04 00:10

    You're using this overload of CreateDelegate:

    Creates a delegate of the specified type to represent the specified static method.

    SomeMethod is not a static method.

    Use an overload that allows to specify a target object:

    A target = new A();
    
    Func<int> f = (Func<int>)Delegate.CreateDelegate(
        typeof(Func<int>),
        target,
        typeof(A).GetMethod(
            "SomeMethod",
            BindingFlags.Instance | BindingFlags.NonPublic));
    

    This means you need to create a delegate for each instance of A though. You cannot reuse the same delegate for different instances.

    The best solution is probably to construct a Lambda Expression using LINQ Expression Trees:

    var p = Expression.Parameter(typeof(A), "arg");
    var lambda = Expression.Lambda<Func<A, int>>(
        Expression.Call(
            p,
            typeof(A).GetMethod(
                "SomeMethod",
                BindingFlags.Instance | BindingFlags.NonPublic)),
        p);
    
    Func<A, int> f = lambda.Compile();
    
    0 讨论(0)
提交回复
热议问题