从非静态方法构建静态委托

时间:2010-11-02 23:32:21

标签: c# delegates static

我需要创建一个类的非静态方法的委托。复杂的是,在创建时我没有对类的重要性,只有它的类定义。在通话时我确实有实例。因此,我需要一种方法:

  1. 为成员方法构建一个“不完整”的委托,缺少实例。
  2. 从1开始调用代理,明确地传递该类的内容。
  3. 这两种可能吗?怎么样? 注意:我愿意为第一名支付高性能价格,但理想情况下2应该不比代表电话贵很多。

5 个答案:

答案 0 :(得分:4)

在给定MethodInfo的情况下,您可以使用Delegate.CreateDelegate为特定目标实例动态构造委托。您可以使用Type.GetMethod(反射)查找MethodInfo并将其缓存以供以后用于创建委托。

例如,这将获取“GetHashCode”方法并将其绑定到“this”实例:

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

如果您有多个方法重载,则会有更多细微之处,但如果需要,还可以使用其他GetMethod参数来消除歧义。

答案 1 :(得分:4)

像这样传递一个实例有什么问题?

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

// Invocation.
bar(new Foo());

它可以满足您的所有需求:它封装了您想要传递的逻辑,并且可以在任意类实例上调用。

编辑:如果您被限制使用某个特定签名的委托(不允许将一个实例作为参数显式传递),那么您可以使用某种形式的“实例提供程序”,该形式是在委托的创建,但可以在以后变异,以便在可用时提供适当的实例,例如:

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();

当然,这有点令人费解,需要传递一个额外的物体,所以也许它并不理想!

答案 2 :(得分:3)

您有两种选择,您可以像对待扩展方法一样对待它。创建一个委托来接受对象和任何可选参数,并将这些参数传递给实际的函数调用。或者像Dan提到的那样使用Delegate.CreateInstance创建一个。

如,

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

答案 3 :(得分:3)

即使这是一篇旧文章,但这里的第三个解决方案是值得的。

我关注创建调度表的一般问题,例如非静态方法的静态查找表。典型的用法可能是ASP.NET事件处理。

我们希望语法和初始化尽可能简单。如果声明和初始化调度表太复杂,那么只编写执行调度的显式If-then-else-if或switch语句会更简单/更安全。

我们可以非常简单地宣布代表集合。 假设一些方法Method1,Method2和一个委托类型为SomeDelegate,那么我们可以写:

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

在这种情况下,我们直接使用方法名称初始化委托。

虽然这很有效,但是一旦我们尝试使dispatchTable成为静态成员,它就会失败。 这是因为SomeDelegate是一个封闭的委托(绑定到实例),因此无法从静态范围初始化。 这是令人沮丧的,因为我们在编译时已知所需的调度,所以理想情况下我们应该能够静态地声明我们的调度表。

正如此线程中所选解决方案中所指出的,您可以通过CreateDelegate创建开放委托,但这在语法上是不方便的,并且还依赖于将方法名称作为字符串传递以创建委托,因此您将失去编译时间检查。使用这种语法声明一个displacch表会非常混乱。

扩展方法技术不那么详细,并且保留了编译时间检查,但与上面的语法相比仍然很难。

另一个(第三个)选项是将已关闭的委托包装在一个(绑定)函数中,该函数给定类实例将返回所需(已关闭)的委托。例如,您可以使用Func。

然后调度表基本上是:

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

假设有一些方法叫做EventHandler1,EventHandler2和它们的委托类型,例如

delegate int EventHandler(string param1, int param2);

然后声明和初始化非静态成员的静态调度表就像这样简单:

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

现在可以通过给定类的实例,处理程序的键和方法参数的调度表来调用方法。

作为示例,从类iteself的成员函数调用,即类实例= this,对于键k,以及参数p1,p2,语法将是:

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

请注意,这会忽略适当的错误检查,例如不存在的密钥。错误检查可以包含在DispatchTable类的GetDelegate方法中。

下面给出一个完整的例子。请注意,它还包含Dictionary类的单独扩展方法,以简化错误处理的语法。

字典扩展名:

    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);
        }                
    };

用法示例:

    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);
        }

    }

答案 4 :(得分:1)

我已经晚了五年,但我只是遇到了这个问题并决定了一个稍微不同的解决方案:

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);
        }
    }

}

使用静态方法访问其实例方法可能适合只需要少量委托方法的任何人。