Build a static delegate from non-static method

后端 未结 5 1147
无人共我
无人共我 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 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 dispatchTable = new Dictionary
    {
        { "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 : Dictionary> 
    

    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
    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(this Dictionary 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 : Dictionary> 
        {
            // 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 { };
    
            // 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);
            }
    
        }
    

提交回复
热议问题