为什么C#不允许静态方法来实现接口?

为什么C#这样设计?

据我所知,一个接口只描述行为,并用于描述实现特定行为的接口类的契约义务。

如果班级希望以共享方式实施这种行为,为什么他们不应该这样做呢?

以下是我想到的一个例子:

// These items will be displayed in a list on the screen.
public interface IListItem {
  string ScreenName();
  ...
}

public class Animal: IListItem {
    // All animals will be called "Animal".
    public static string ScreenName() {
        return "Animal";
    }
....
}

public class Person: IListItem {

    private string name;

    // All persons will be called by their individual names.
    public string ScreenName() {
        return name;
    }

    ....

 }

假设你问为什么你不能这样做:

public interface IFoo {
    void Bar();
}

public class Foo: IFoo {
    public static void Bar() {}
}

这在语义上对我没有意义。 在接口上指定的方法应该在那里指定与对象交互的协定。 静态方法不允许你与对象交互 - 如果你发现你的实现可以变为静态,你可能需要问自己这个方法是否真的属于接口。


为了实现你的例子,我会给Animal一个const属性,它仍然允许它从一个静态上下文访问,并在实现中返回该值。

public class Animal: IListItem {
    /* Can be tough to come up with a different, yet meaningful name!
     * A different casing convention, like Java has, would help here.
     */
    public const string AnimalScreenName = "Animal";
    public string ScreenName(){ return AnimalScreenName; }
}

对于更复杂的情况,你总是可以声明另一个静态方法并委托给它。 在试图拿出一个例子时,我想不出任何理由你会在静态和实例上下文中做一些不平凡的事情,所以我会给你一个FooBar blob,并将其作为一个迹象表明它可能会不是一个好主意。


我的(简化的)技术原因是静态方法不在vtable中,并且在编译时选择调用站点。 这是你不能拥有重写或虚拟静态成员的原因。 有关更多详细信息,您需要CS毕业生或编译器wonk - 我既不是。

出于政治原因,我会引用Eric Lippert(他是一名编译者,并拥有滑铁卢大学数学,计算机科学和应用数学学士学位(来源:LinkedIn):

...静态方法的核心设计原则,这个原则给了它们他们的名字......它总是可以在编译时被精确确定,调用什么方法。 也就是说,该方法可以通过对代码的静态分析完全解决。

请注意,Lippert为所谓的类型方法留出了空间:

也就是说,与类型(如静态)相关联的方法,它不采用不可空的“this”参数(与实例或虚拟不同),而是所调用的方法取决于构造类型T(不像静态,它必须在编译时确定)。

但尚未确定其用处。


这里的大多数答案似乎都忽略了整个观点。 多态性不仅可以在实例之间使用,也可以在类型之间使用。 当我们使用泛型时,这通常是需要的。

假设我们在泛型方法中有类型参数,我们需要对它进行一些操作。 我们不想要瞬间,因为我们不知道构造函数。

例如:

Repository GetRepository<T>()
{
  //need to call T.IsQueryable, but can't!!!
  //need to call T.RowCount
  //need to call T.DoSomeStaticMath(int param)
}

...
var r = GetRepository<Customer>()

不幸的是,我只能拿出“丑陋”的选择:

  • 使用反射丑陋,打破接口和多态的想法。

  • 创建完全独立的工厂类

    这可能会大大增加代码的复杂性。 例如,如果我们试图为域对象建模,则每个对象都需要另一个存储库类。

  • 实例化,然后调用所需的接口方法

    即使我们控制类的来源,用作通用参数,这也很难实现。 原因在于,例如,我们可能需要实例仅处于众所周知的“连接到数据库”状态。

  • 例:

    public class Customer 
    {
      //create new customer
      public Customer(Transaction t) { ... }
    
      //open existing customer
      public Customer(Transaction t, int id) { ... }
    
      void SomeOtherMethod() 
      { 
        //do work...
      }
    }
    

    为了使用instantine来解决静态接口问题,我们需要做以下事情:

    public class Customer: IDoSomeStaticMath
    {
      //create new customer
      public Customer(Transaction t) { ... }
    
      //open existing customer
      public Customer(Transaction t, int id) { ... }
    
      //dummy instance
      public Customer() { IsDummy = true; }
    
      int DoSomeStaticMath(int a) { }
    
      void SomeOtherMethod() 
      { 
        if(!IsDummy) 
        {
          //do work...
        }
      }
    }
    

    这显然很难看,也没有必要使所有其他方法的代码复杂化。 显然,这不是一个优雅的解决方案!

    链接地址: http://www.djcxy.com/p/38147.html

    上一篇: Why Doesn't C# Allow Static Methods to Implement an Interface?

    下一篇: Inheritance and interfaces