在下面的代码中我定义了一个接口,一个抽象基类,带有一个打印“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();
}
}
答案 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