我一直在使用代表多年,并没有真正考虑过他们。
但是我最近通过假设代表在引用类方法时在签名中包含this
引用而得到了鸡蛋。
以下示例说明了我理解中的差距。
public class SomeClass
{
public SomeClass(int someProperty)
{
SomeProperty = someProperty;
}
public int SomeProperty
{
get;
set;
}
// Throw in a Member field into the mix
public int ClassAdd(int x, int y)
{
return x + y + SomeProperty;
}
}
public static class SomeStaticClass
{
public static int StaticAdd(int x, int y)
{
return x + y;
}
}
为什么我可以添加静态和实例订阅者?
delegate int addDelegate(int x, int y);
class TestClass
{
delegate int addDelegate(int x, int y);
private void useDelegates()
{
addDelegate algorithm;
algorithm = SomeStaticClass.StaticAdd;
algorithm += new SomeClass(3).ClassAdd;
int answer = algorithm(5, 10);
}
}
实际上是什么? ;)
答案 0 :(得分:4)
如果您创建一个引用实例方法的委托,它将在支持委托的Target
属性的字段中捕获this
(或相关引用)。如果创建引用静态方法的委托,则Target
将为null。从逻辑上讲,如果你使用静态方法,则不需要有实例。
作为一个增加的复杂功能,您可以捕获扩展方法,就像它们是扩展类型上的实例方法一样:
static class Extensions
{
public static void Foo(this string x)
{
Console.WriteLine("Calling Foo on " + x);
}
}
class Test
{
static void Main()
{
Action action = "text".Foo;
Console.WriteLine(action.Target); // Prints "text"
}
}
至于为什么你可以做所有这些:因为它很有用,并且没有理由不允许你这样做:)
答案 1 :(得分:1)
C#中没有静态函数,它们实际上是静态方法(因此有静态和实例方法)。每个委托基本上都是指向方法的this
指针的指针,它是委托的Target属性。在静态方法的情况下,它将为null。由于委托携带对象的引用,如果您忘记取消订阅,它可能会导致内存泄漏。
答案 2 :(得分:1)
代表们的美妙之处在于它们只适用于静态和实例方法。如果为委托分配静态方法,则静态方法只是按原样使用,因为它不需要任何非静态上下文。对于实例方法,对实例的引用沿着方法指针存储,以便在调用方法时可用的必要上下文(this
引用)。这意味着当您将实例方法分配给委托时,您还必须提供一个实例 - 通过显式指定它,或者通过从实例上下文中指定委托。
大多数时候,这只是“有效”,你不需要考虑任何事情。但有一点需要注意:如果您将一个实例方法分配给一个委托,那么您将创建另一个对该实例的引用,并且只要该方法仍然分配给该委托(或订阅了一个事件),也会有一个引用实例,因此GC永远不会收集它。这就是为什么在自己清理之后应该总是取消订阅所有事件的原因。