【问题标题】:extension method overriding inherited method扩展方法覆盖继承方法
【发布时间】:2011-09-05 10:52:36
【问题描述】:

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

【问题讨论】:

    标签: c# .net extension-methods


    【解决方案1】:

    ISomething 没有名为foo 的成员,所以会调用扩展方法。

    【讨论】:

      【解决方案2】:

      因为变量被声明为ISomething

      实例方法直到运行时才知道,但是方法重载决议是在编译时。不能保证实例实际上有合适的方法。在您的特定示例中,它具有但从类型安全的角度来看,这是一个无关紧要的巧合。 g.foo() 行的重要类型是 g 被声明为不是运行时类型的类型

      【讨论】:

      • 我接受这个答案,因为对我来说,这是解释这个问题中继承和扩展方法背后的基本原理的最容易理解的答案。
      【解决方案3】:

      SomethingElse 上的 foo 方法不会被考虑用于重载解决方案,因为您正在处理 ISomething 的实例。

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

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

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

      它的行为应该符合您的预期。

      【讨论】:

        【解决方案4】:

        此时调用ISomethingfoo方法,解析为扩展方法。

        如果您使用此代码:

        ISomething g = new DefinitelySomething();
        (g as SomethingElse).foo();
        

        你会得到正确的输出。

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

            【解决方案6】:

            还有一些示例,在 LinqPad 中进行了测试。 注意声明为ISomething的项目使用了扩展方法,返回"bar",其中@leppiepointed out是因为ISomething没有方法foo,而是调用@新实例上的 987654325@ 直接给出正确的输出 "foo"

            void Main(string[] args) {
                    // baseline
                    var g0 = new DefinitelyNothing();                   g0.foo(); // "foo"
            
                    ISomething g;
            
                    // won't work, not ISomething
                    // g = new DefinitelyNothing();     g.foo();
            
                    g = new DefinitelyNothingThatThinksItsSomething();  g.foo(); // "bar"
                        new DefinitelyNothingThatThinksItsSomething().foo();    // "foo"
            
                    g = new DefinitelySomething();                      g.foo(); // "bar"
                        new DefinitelySomething().foo();                        // "foo"
            
                    g = new DefinitelySomethingWithFoo();               g.foo(); // "bar"
                        new DefinitelySomethingWithFoo().foo();                 // "foo"
                        (g as IWithFoo).foo();                                  // "foo", not "qux"
            
                    g = new DefinitelySomethingFromNothingWithFoo();    g.foo(); // "bar"
                        new DefinitelySomethingFromNothingWithFoo().foo();      // "foo"
                        (g as ISomethingWithFoo).foo();                         // "foo", not "baz"
            
                    IWithFoo g1;
                    g1 = new DefinitelyNothingWithFoo();                g1.foo(); // "foo"
                        new DefinitelyNothingWithFoo().foo();                    // "foo"
            }
            
            public interface ISomething {}
            public interface IWithFoo {
                void foo();
            }
            public interface ISomethingWithFoo : ISomething, IWithFoo {}
            
            public abstract class Nothing {
                public void foo() { Console.WriteLine("foo"); }
            }
            public abstract class NothingWithFoo : IWithFoo {
                public void foo() { Console.WriteLine("foo"); }
            }
            public abstract class Something : ISomething {
                public void foo() { Console.WriteLine("foo"); }
            }
            public abstract class SomethingWithFoo : ISomethingWithFoo {
                public void foo() { Console.WriteLine("foo"); }
            }
            public abstract class SomethingFromNothingWithFoo : Nothing, ISomethingWithFoo {}
            
            public class DefinitelyNothing: Nothing {}
            public class DefinitelyNothingThatThinksItsSomething: Nothing, ISomething {}
            public class DefinitelyNothingWithFoo : NothingWithFoo {}
            
            public class DefinitelySomething : Something {}
            public class DefinitelySomethingWithFoo : SomethingWithFoo {}
            public class DefinitelySomethingFromNothingWithFoo : SomethingFromNothingWithFoo {}
            
            
            public static class ISomethingExtensions {
                // http://en.wikipedia.org/wiki/Metasyntactic_variable
                public static void foo(this ISomething whatever) { Console.WriteLine("bar"); }
                public static void foo(this ISomethingWithFoo whatever) { Console.WriteLine("baz"); }
                public static void foo(this IWithFoo whatever) { Console.WriteLine("qux"); }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-11-20
              • 1970-01-01
              • 2017-03-16
              • 2010-11-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-06-04
              相关资源
              最近更新 更多