为什么委托可以互换地用于Class方法和Static函数?

时间:2010-08-10 08:54:27

标签: c# delegates

我一直在使用代表多年,并没有真正考虑过他们。 但是我最近通过假设代表在引用类方法时在签名中包含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);
        }
    }

实际上是什么? ;)

3 个答案:

答案 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永远不会收集它。这就是为什么在自己清理之后应该总是取消订阅所有事件的原因。