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

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

  1. 为成员方法构建一个“不完整”的委托,缺少实例。
  2. 从1中调用委托明确地传递该类的内容。

这些都可能吗? 怎么样? 注意:我愿意为第一名支付高性能价格,但理想情况下2不应该比委托电话贵很多。

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

例如,

 string s = "foobar"; // "extension method" approach Func 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)Delegate.CreateDelegate(typeof(Func), 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)Delegate.CreateDelegate(typeof(Func), s, "Substring"); substring3(3); // "bar" // for a static method var compare = (Func)Delegate.CreateDelegate(typeof(Func), typeof(string), "Compare"); compare(s, "zoobar"); // -1 

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

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

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

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

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

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

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

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

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

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

虽然这是一个老post,但这里值得的是第三个解决方案。

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

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

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

 Dictionary dispatchTable = new Dictionary { { "Key1", Method1 } ,{ "Key2", Method2 } .... } 

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

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

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

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

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

然后调度表基本上是:

 public class DispatchTable : Dictionary> 

假设一些方法称为EventHandler1,EventHandler2和它们的委托类型,例如

 delegate int EventHandler(string param1, int param2); 

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

 class MyDispatchTable : DispatchTable 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类的单独扩展方法,以简化error handling的语法。

字典扩展:

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

调度表类:

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

用法示例:

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

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

 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 ListOfProcessing = new List() { ProcessStuffA, ProcessStuffB // ProcessStuffC etc }; public DelEx() { foreach (ProcessStuffDelegate processStuffDelegate in ListOfProcessing) { processStuffDelegate(this); } } } 

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