Build a static delegate from non-static method

后端 未结 5 1148
无人共我
无人共我 2021-01-06 08:28

I need to create a delegate to a non-static method of a class. The complications is that at the time of creation I don\'t have an intance to the class, only its class defini

相关标签:
5条回答
  • 2021-01-06 08:52

    You have two options, you can treat it like you would an extension method. Create an delegate to take in the object and any optional arguments and pass those arguments to the actual function call. Or create one using Delegate.CreateInstance as Dan mentioned.

    e.g.,

    string s = "foobar";
    
    // "extension method" approach
    Func<string, int, string> substring1 = (s, startIndex) => s.Substring(startIndex);
    substring1(s, 1); // "oobar"
    
    // using Delegate.CreateDelegate
    var stype = typeof(string);
    var mi = stype.GetMethod("Substring", new[] { typeof(int) });
    var substring2 = (Func<string, int, string>)Delegate.CreateDelegate(typeof(Func<string, int, string>), mi);
    substring2(s, 2); // "obar"
    
    // it isn't even necessary to obtain the MethodInfo, the overload will determine
    // the appropriate method from the delegate type and name (as done in example 2).
    var substring3 = (Func<int, string>)Delegate.CreateDelegate(typeof(Func<int, string>), s, "Substring");
    substring3(3); // "bar"
    
    // for a static method
    var compare = (Func<string, string, int>)Delegate.CreateDelegate(typeof(Func<string, string, int>), typeof(string), "Compare");
    compare(s, "zoobar"); // -1
    
    0 讨论(0)
  • 2021-01-06 08:57

    You can use Delegate.CreateDelegate to dynamically construct a delegate for a particular target instance given a MethodInfo. You can look up the MethodInfo with Type.GetMethod (Reflection) and cache it for later use creating the delegate.

    For example, this would grab the "GetHashCode" method and bind it to the 'this' instance:

            var method = typeof(Object).GetMethod("GetHashCode");
            var del = (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), this, method);
    

    There are more subtleties if you have more than one overload of the method, but there are additional GetMethod parameters you can use to disambiguate, if necessary.

    0 讨论(0)
  • 2021-01-06 09:03

    What's wrong with just passing an instance in like this?

    // Creation.
    Action<Foo> bar = foo =>
    {
        foo.Baz();
    };
    
    // Invocation.
    bar(new Foo());
    

    It does all you need it to: it encapsulates the logic you want to pass around, and can be invoked on an arbitrary class instance.

    Edit: If you're restricted to using a delegate of a certain signature (not allowing an instance to be passed explicitly as a parameter), then you could use some form of "instance provider" which is specified at the time of the delegate's creation, but can be mutated later to provide the appropriate instance when it becomes available, e.g.:

    class Provider<T>
    {
        public T Instance { get; set; }
    }
    
    static Action Create(Provider<Foo> provider)
    {
        return () =>
        {
            provider.Instance.Baz();
        };
    }
    
    // ...
    
    // Creation.
    var provider = new Provider<Foo>();
    var bar = Create(provider);
    
    // Invocation.
    provider.Instance = new Foo();
    bar();
    

    Of course, this is a bit convoluted, and requires an additional object to be passed around, so perhaps it's not ideal!

    0 讨论(0)
  • 2021-01-06 09:05

    Even though this is an old post, for what it is worth here is a third solution.

    I was concerned with the general problem of creating dispatch tables e.g. static lookup tables of non-static methods. A typical use might be in ASP.NET event handling.

    We would like the syntax and initialization to be as simple as possible. If declaring and initializing a dispatch table is too complicated then it would be simpler/safer to just write the explicit If-then-else-if or switch statement that does the dispatch.

    We can declare a collection of delegates very simply indeed. Assuming some methods Method1, Method2 and a delegate type SomeDelegate for them, then we can write:

    Dictionary<string, SomeDelegate> dispatchTable = new Dictionary<string, SomeDelegate>
    {
        { "Key1", Method1 }
        ,{ "Key2", Method2 }
       ....
    }
    

    In this case we are initializing the delegates using the method name directly.

    While this works unfortunately it will fail as soon as we try to make the dispatchTable a static member. This is because SomeDelegate is a closed delegate (bound to an instance) and thus cannot be initialized from a static scope. This is frustrating as our required dispatches are known at compile time so ideally we should be able to declare our dispatch table statically.

    As pointed out in the selected solution in this thread, you can create open delegates via CreateDelegate but this is syntactically awkward and also relies on passing the method name as a string to create the delegate so you lose compile time checking. Declaring a displatch table using this syntax would be very messy.

    The extension method technique is less verbose and retains compile time checking, but it is still awkward compared to the syntax above.

    Another (third) option is to wrap the closed delegates in a (binding) function that given the class instance will return the desired (closed) delegate. For example you can use Func.

    Then the dispatch table is basically:

    public class DispatchTable<Class, Key, Delegate> : Dictionary<Key, Func<Class, Delegate>> 
    

    Assuming some methods called EventHandler1, EventHandler2 and a delegate type for them e.g.

    delegate int EventHandler(string param1, int param2);
    

    then declaring and intializing a static dispatch table of non-static members is as simple as:

    class MyDispatchTable : DispatchTable<MyClass, string, EventHandler>
    static MyDispatchTable dispatchTable = new MyDispatchTable
    {
        { "Event1", c => c.EventHandler1 }
        ,{ "Event2", c => c.EventHandler2 }
    }; 
    

    Methods can now be invoked via the dispatch table given an instance of the class, a key for the handler and the method parameters.

    As an example, invoking from a member function of the class iteself i.e. the class instance = this, for a key k, and parameters p1, p2 the syntax would be:

    var result = dispatchTable[key](this)(p1, p2);
    

    Note that this ignores appropriate error checking e.g. non-existent keys. The error checking can be wrapped up in a GetDelegate method on the DispatchTable class.

    A full example is given below. Note that it also includes a seperate extension method for the Dictionary class to simplify the syntax for error handling.

    Dictionary extension:

        static public class DictionaryExtensions
        {
            // extension method to simplify accessing a dictionary 
            static public V GetValueOrDefault<K, V>(this Dictionary<K, V> dict, K key)
            {
                V value;
                dict.TryGetValue(key, out value);
                return value;
            }
        }
    

    Dispatch Table class:

        // Syntactic sugar for declaring a general dispatch table
        // The dictionary maps from a key to a function that can return 
        // a closed delegate given an instance of a class.
        // Note that if keys are always integers then it is simpler to use an
        // array rather than a dictionary.
        public class DispatchTable<Key, Class, Delegate> : Dictionary<Key, Func<Class, Delegate>> 
        {
            // standardise the method for accessing a delegate
            public Delegate GetDelegate(Class c, Key k)
            {
                var d = GetValueOrDefault(k);
                if (d == null)
                {
                    throw new ArgumentException(String.Format("Delegate not found for key [{0}]",k));
                }
                return d(c);
            }                
        };
    

    Usage example:

        public class Test
        {
            // some member functions to invoke
            public int EventHandler1(string param1, int param2) { return 1; }
            public int EventHandler2(string param1, int param2) { return 2; }
    
            // Declaration for a (closed) delegate for the member functions
            private delegate int EventHandler(string param1, int param2);
    
            // Syntactic sugar for declaring the table 
            private class EventDispatchTable : DispatchTable<string, Test, EventHandler> { };
    
            // Declare dispatch table and initialize 
            static EventDispatchTable dispatchTable = new EventDispatchTable
            {
                { "Event1", c => c.EventHandler1 }
                ,{ "Event2", c => c.EventHandler2 }
            };
    
            // Invoke via the dispatch table
            public int DoDispatch(string eventName, string param1, int param2)
            {
                return dispatchTable.GetDelegate(this, eventName)(param1, param2);
            }
    
        }
    
    0 讨论(0)
  • 2021-01-06 09:11

    I'm five years late to the party but I've only just come across this problem and decided on a slightly different solution:

    public class DelEx
    {
        private delegate void ProcessStuffDelegate(DelEx me);
    
        private static void ProcessStuffA(DelEx me)
        {
            me.ProcessStuffA();
        }
    
        private void ProcessStuffA()
        {
            // do tricky A stuff
        }
    
        private static void ProcessStuffB(DelEx me)
        {
            me.ProcessStuffB();
        }
    
        private void ProcessStuffB()
        {
            // do tricky B stuff
        }
    
        private readonly static List<ProcessStuffDelegate> ListOfProcessing = new List<ProcessStuffDelegate>()
        {
            ProcessStuffA,
            ProcessStuffB
            // ProcessStuffC etc
        };
    
        public DelEx()
        {
            foreach (ProcessStuffDelegate processStuffDelegate in ListOfProcessing)
            {
                processStuffDelegate(this);
            }
        }
    
    }
    

    Using static methods to access their instance methods may be suitable for anyone requiring only a few delegate methods.

    0 讨论(0)
提交回复
热议问题