扩展方法重写继承的方法

时间:2011-09-05 10:52:36

标签: c# .net extension-methods

在下面的代码中我定义了一个接口,一个抽象基类,带有一个打印“foo”的方法,一个实现它们的类,以及一个接口上的扩展方法,其签名等于抽象基类中的方法打印“酒吧”。当我运行这个样本时,为什么打印“bar”而不是“foo”?如果适用,这种语言设计选择背后的士气是什么?

public interface ISomething
{}

public abstract class SomethingElse
{
    public void foo()
    {
        Console.WriteLine("foo");
    }
}

public class DefinitelySomething : SomethingElse, ISomething
{}

public static class ISomethingExtensions
{
    public static void foo(this ISomething graphic)
    {
        Console.WriteLine("bar");
    }
}

class Program
{
    static void Main(string[] args)
    {
        ISomething g = new DefinitelySomething();
        g.foo();
    }
}

6 个答案:

答案 0 :(得分:9)

ISomething没有名为foo的成员,因此将调用扩展方法。

答案 1 :(得分:5)

因为变量声明为ISomething

实例方法直到运行时才知道,但方法重载解析是在编译时。无法保证实例实际上有合适的方法。在你的特定例子中,它具有但从类型安全角度来看是无关紧要的巧合。第g.foo()行的重要类型是g 声明的类型,而不是运行时类型

答案 2 :(得分:1)

foo上的SomethingElse方法不会被视为重载解析,因为您正在对ISomething的实例执行操作。

或者以另一种方式思考,考虑如果你没有任何扩展方法会发生什么?在这种情况下,您将收到编译错误,因为找不到其他合适的方法。

尝试将代码更改为以下内容:

DefinitelySomething g = new DefinitelySomething();
g.foo();

它应该表现出你的期望。

答案 3 :(得分:0)

在这种情况下,您调用foo的{​​{1}}方法,并将其解析为扩展方法。

如果您使用此代码:

ISomething

你会得到正确的输出。

答案 4 :(得分:0)

您可以随时检查扩展方法,看看您当前使用的对象是否具有完全相同签名的现有方法

    public static System.Reflection.MethodInfo ExtensionOverrider(this Object obj, System.Reflection.MethodInfo method )
    {
        return obj.GetType().GetMethods().Where(
            x => x.Name == method.Name &&
            x.GetParameters().Select(z => z.ParameterType).SequenceEqual(method.GetParameters().Skip(1).Select(w => w.ParameterType))).FirstOrDefault(); 
    }


    public static void foo(this ISomething graphic)
    {
        var Method = graphic.ExtensionOverrider(System.Reflection.MethodBase.GetCurrentMethod());                
        if( Method != null)
            Method.Invoke(graphic, new Object[0]{});
        else
            Console.WriteLine("bar");
    }

答案 5 :(得分:-1)

在LinqPad中测试的更多示例。 请注意,声明为ISomething的项目使用扩展方法并返回“bar”,因为ISomething没有方法{@leppie pointed out。 {1}},但在新实例上调用foo直接提供正确的输出“foo”

foo