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


447

为什么用这种方式设计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;
    }

    ....

 }

6
好吧,Java 8拥有了它(stackoverflow.com/questions/23148471/…)。

1
了解如何将静态行为与继承或接口实现相结合:stackoverflow.com/a/13567309/880990
Olivier Jacot-Descombes

1
IListItem.ScreenName() => ScreenName()(使用C#7语法)将通过调用static方法显式实现接口方法。但是,当您向其中添加继承时,事情变得很丑陋(必须重新实现接口)
Jeroen Mostert

2
只是让大家知道等待已经结束!C#8.0具有静态接口方法:dotnetfiddle.net/Lrzy6y(尽管它们的工作方式与OP希望它们工作的方式略有不同-您不必实现它们)
Jamie Twells

Answers:


221

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

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,并以此来表明它可能不是一个好主意。


6
完美的例子!但我不确定我是否理解您的理由。当然可以设计编译器来查看statuc成员吗?实例是否没有用于实现theis方法的地址表?表格中不能包含静态方法吗?
Kramii

12
在某些情况下这可能很有用。例如,我希望所有实现者实现带有XElement参数的GetInstance方法。我既不能将其定义为接口中的静态方法,也不能要求接口提供构造函数签名。
oleks 2011年

25
很多人都在权衡利弊:从“这没有意义”到“这是一个错误,我希望你能做到”。(我认为这里有有效的用例,这就是我到这里为止的方式。)作为一种解决方法,实例方法可以简单地委派给静态方法。
哈波

5
您也可以简单地将片段作为扩展方法实现到基本接口上,例如:public static object MethodName(this IBaseClass base)在静态类中。但是,不利的一面是,与接口继承不同,它不会强制/不允许单个继承者很好地重写方法。
Troy Alford 2012年

8
使用泛型将很有意义。例如void Something <T>()其中T:ISomeInterface {new T()。DoSomething1(); T.DoSomething2(); }
Francisco Ryan Tolmasky I 13/12/18

173

我的(简化的)技术原因是vtable中没有静态方法,并且在编译时选择了调用站点。这是您不能具有覆盖成员或虚拟静态成员的相同原因。有关更多详细信息,您需要CS毕业生或编译器专家,但我都不是。

出于政治原因,我将引用埃里克·利珀特Eric Lippert)(他是一名编译器专家,并拥有滑铁卢大学的数学,计算机科学和应用数学学士学位)(来源:LinkedIn):

...静态方法的核心设计原理,为其赋予名称的原理...在编译时始终可以准确确定将调用哪种方法。也就是说,该方法只能通过代码的静态分析来解决。

请注意,Lippert确实为所谓的type方法留出了空间:

也就是说,与一种类型(如静态类型)相关联的方法,该方法不采用不可为空的“ this”自变量(与实例或虚拟方法不同),但是所调用的方法将取决于构造的T类型(与静态变量不同,静态变量必须在编译时才能确定)。

但尚未确信其有用性。


5
太好了,这是我要写的答案-我只是不知道实现细节。
克里斯·玛拉斯蒂·乔治

5
好答案。我想要这种“类型方法”!在很多情况下会很有用(考虑类型/类的元数据)。
菲利普·道布迈尔2012年

23
这是正确的答案。您将接口传递给某人时,他们需要知道如何调用方法。接口只是一个虚拟方法表。您的静态类没有那个。调用者不会知道如何调用方法。(在我读完这个答案之前,我认为C#只是一个书呆子。现在我意识到这是一个技术限制,具体取决于接口什么)。其他人会和你说说这是一个糟糕的设计。这不是一个糟糕的设计-这是技术限制。
伊恩·博伊德

2
+1用于实际回答问题,而不是学究和流鼻涕。就像伊恩·博伊德(Ian Boyd)所说:“这不是一个糟糕的设计-这是技术限制。”
约书亚·佩奇

3
完全有可能为带有关联的vtable的静态类生成一个对象。查看Scala如何处理objects以及如何允许它们实现接口。
塞巴斯蒂安·格拉夫

97

这里的大多数答案似乎都没有抓住重点。多态不仅可以在实例之间使用,还可以在类型之间使用。当我们使用泛型时,通常需要这样做。

假设我们在通用方法中有类型参数,并且需要对其进行一些操作。我们不想实例化,因为我们不知道构造函数。

例如:

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...
  }
}

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

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...
    }
  }
}

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


35
+1表示“这里的大多数答案似乎遗漏了所有要点”。令人难以置信的是,似乎所有答案几乎都

5
@Chris这是一个具体示例,使我再次达到了此限制。我想向类添加IResettable接口,以指示它们将某些数据缓存在可由站点管理员重置的静态变量中(例如,订单类别列表,增值税率集合,从外部API检索的类别列表)为了减少数据库和外部API的损失,这显然将使用静态方法重置。这使我可以自动检测可以重置哪些类。我仍然可以执行此操作,但是该方法未在IDE中强制执行或自动添加,而是依赖于希望。
mattmanser 2012年

6
@克里斯我不同意,大刀阔斧。当更多的体系结构是“最佳”解决方案时,这始终是语言缺陷的迹象。还记得自C#获得泛型和匿名方法以来没有人谈论的所有模式吗?
mattmanser 2012年

3
您不能使用“ where T:IQueryable,T:IDoSomeStaticMath”或类似名称吗?
Roger Willcocks 2012年

2
@ ChrisMarasti-Georg:有点肮脏但有趣的方法是这种小结构:public abstract class DBObject<T> where T : DBObject<T>, new(),然后使所有DB类都继承为DBObject<T>。然后,可以在抽象超类中使按键检索成为返回类型为T的静态函数,使该函数创建T的新对象,然后String GetRetrieveByKeyQuery()对该对象调用受保护的对象(在超类上定义为抽象)以获取要执行的实际查询。尽管这可能会引起一些
争议

20

我知道这是一个古老的问题,但这很有趣。这个例子不是最好的。我认为如果显示用例会更清楚:

字符串DoSomething <T>(),其中T:ISomeFunction
{
  如果(T.someFunction())
    ...
}

仅仅让静态方法实现接口将无法实现您想要的;需要的是将静态成员作为接口的一部分。我当然可以想象有很多用例,尤其是在能够创建事物方面。我可以提供两种可能有用的方法:

  1. 创建一个静态泛型类,其类型参数将是您要传递给上面的DoSomething的类型。此类的每个变体都将具有一个或多个静态成员,这些成员包含与该类型有关的内容。可以通过使每个感兴趣的类调用“注册信息”例程来提供此信息,也可以通过在运行类变体的静态构造函数时使用反射来获取信息。我相信后一种方法被Comparer <T> .Default()之类的东西使用。
  2. 对于每个感兴趣的类T,定义一个实现IGetWhateverClassInfo <T>并满足“新”约束的类或结构。该类实际上将不包含任何字段,但是将具有一个静态属性,该属性将返回带有类型信息的静态字段。将该类或结构的类型传递给所讨论的通用例程,该例程将能够创建实例并使用它来获取有关其他类的信息。如果为此目的使用一个类,则可能应如上所述定义一个静态的通用类,以避免每次都必须构造一个新的描述符对象实例。如果使用结构,则实例化成本应该为零,但是每种不同的结构类型都需要对DoSomething例程进行不同的扩展。

这些方法都没有真正吸引人的地方。另一方面,我希望如果CLR中存在能够干净地提供这种功能的机制,.net将允许人们指定参数化的“新”约束(因为知道类是否具有带有特定签名的构造函数,这似乎是合理的)。可以比拟知道它是否具有带有特定签名的静态方法的难度)。


16

我猜是短视的。

最初设计时,接口只能与类的实例一起使用

IMyInterface val = GetObjectImplementingIMyInterface();
val.SomeThingDefinedinInterface();

只是随着接口的引入,因为泛型的约束确实在接口中添加了静态方法才具有实际用途。

(回应评论:)我认为现在对其进行更改将需要对CLR进行更改,这将导致与现有程序集的不兼容。


我是在泛型的上下文中首次遇到此问题的,但我想知道在接口中包含静态方法在其他上下文中是否也有用?有什么原因不能改变一切吗?
Kramii

在实现通用类时,我也遇到了这种情况,该类需要参数类型来创建带有一些参数的自身。由于new()不能接受任何内容。您知道吗,Kramii?
汤姆”,

1
@Kramii:静态API的合同。我不想要一个对象实例,只是一个特定签名的保证,例如。IMatrixMultiplier或ICustomSerializer。作为类成员的Funcs / Actions / Delegates可以做到这一点,但是IMO有时似乎过于矫kill过正,并且对于没有经验的扩展API可能会感到困惑。
David Cuccia 2010年

14

接口指定对象的行为。

静态方法不指定对象的行为,而是以某种方式影响对象的行为。


71
对不起..我不确定是对的!接口未指定行为。接口定义了一组命名操作。两个类可以实现接口的方法,使其行为完全不同。因此,接口根本不指定行为。实现它的类可以。
Scott Langham

1
希望您不要认为我很挑剔..但是,我认为这是任何学习OO的人都可以理解的重要区别。
Scott Langham

4
应该假定一个接口指定包含行为和表示的合同。这就是为什么更改接口调用的行为是不行的原因,因为两者都应该是固定的。如果您有一个接口,其中调用的行为有所不同(即IList.Add进行了删除),那将是不对的。
Jeff Yates

14
好吧,是的,您可能会想办法定义一个名称不相符的方法。如果您有IAlertService.GetAssistance(),它的行为可能是闪烁灯光,发出警报或用棍子戳眼睛。
Scott Langham

1
实现也将能够写入日志文件。在界面中未指定此行为。但是,也许你是对的。真正应该尊重“获得帮助”的行为。
Scott Langham

14

就接口表示“合同”而言,静态类实现接口似乎很合理。

上述论点似乎都错过了关于合同的这一点。


2
我完全同意这个简单但有效的答案。在“静态接口”中有趣的是它将代表合同。也许它不应该被称为“静态接口”,但是我们仍然会错过一个构造。例如,检查有关ICustomMarshaler接口的.NET官方文档。它要求实现它的类“添加一个名为GetInstance的静态方法,该方法接受String作为参数,并具有ICustomMarshaler的返回类型”。确实确实看起来像普通英语中的“静态接口”定义,而我更喜欢用C#...
Simon Mourier 2015年

@SimonMourier该文档本来可以写得更清楚,但是您误解了它。不是ICustomMarshaler接口需要GetInstance静态方法。这是[MarshalAs]代码属性所需要的。他们使用工厂模式来允许属性获取附加封送处理程序的实例。不幸的是,他们完全忘记在MarshalAs文档页面上包含对GetInstance要求的文档记录(它仅显示使用内置封送处理实现的示例)。
Scott Gartner

@ScottGartner-明白你的意思。msdn.microsoft.com/zh-cn/library/…明确指出:“除了实现ICustomMarshaler接口之外,自定义封送程序还必须实现一个名为GetInstance的静态方法,该方法接受String作为参数,并具有ICustomMarshaler的返回类型。公共语言运行时的COM互操作层调用static方法以实例化自定义封送拆收器的实例。” 这绝对是静态合同定义。
西蒙·穆里尔

9

因为接口的目的是允许多态,所以能够传递所有已经定义好的定义类的实例来实现定义的接口...保证在您的多态调用中,代码将能够找到您正在调用的方法。允许静态方法实现接口是没有意义的,

你怎么称呼它?


public interface MyInterface { void MyMethod(); }
public class MyClass: MyInterface
{
    public static void MyMethod() { //Do Something; }
}

 // inside of some other class ...  
 // How would you call the method on the interface ???
    MyClass.MyMethod();  // this calls the method normally 
                         // not through the interface...

    // This next fails you can't cast a classname to a different type... 
    // Only instances can be Cast to a different type...
    MyInterface myItf = MyClass as MyInterface;  

1
其他语言(例如Java)允许从对象实例中调用静态方法,尽管您会得到警告,应该从静态上下文中调用它们。
克里斯·玛拉斯蒂·乔治

.Net中也允许从实例调用静态方法。那是另一回事。如果静态方法实现了接口,则可以在没有实例的情况下调用它。那是没有意义的。请记住,您不能将实现放在接口中,它必须在类中。因此,如果定义了五个不同的类来实现该接口,并且每个类都有此静态方法的不同实现,则编译器将使用哪个类?
查尔斯·布雷塔纳

1
在泛型的情况下,@ CharlesBretana是传入的类型。我看到为静态方法提供接口很有用(或者,如果您愿意,我们可以将它们称为“静态接口”,并允许类定义两个静态接口)和实例接口)。因此,如果有的话,Whatever<T>() where T:IMyStaticInterface我可以在实例中调用Whatever<MyClass>()并包含T.MyStaticMethod()Whatever<T>()实现中。调用方法将在运行时确定。您可以通过反思来做到这一点,但是没有强制执行的“合同”。
Jcl 2015年

4

关于在非泛型上下文中使用的静态方法,我同意在接口中允许它们是没有任何意义的,因为如果仍然引用了该接口,则将无法调用它们。但是,通过在非多态上下文中使用接口而不是在通用上下文中创建的接口在语言设计中存在一个根本漏洞。在这种情况下,接口根本不是接口,而是约束。由于C#在接口外部没有约束的概念,因此缺少实质性的功能。例子:

T SumElements<T>(T initVal, T[] values)
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

这里没有多态性,泛型使用对象的实际类型并调用+ =运算符,但这失败了,因为无法确定该运算符是否存在。一种简单的解决方案是在约束中指定它。简单的解决方案是不可能的,因为运算符是静态的,并且静态方法不能在接口中,并且(这里是问题)约束表示为接口。

C#需要的是真正的约束类型,所有接口也将是约束,但是并非所有约束都将是接口,那么您可以这样做:

constraint CHasPlusEquals
{
    static CHasPlusEquals operator + (CHasPlusEquals a, CHasPlusEquals b);
}

T SumElements<T>(T initVal, T[] values) where T : CHasPlusEquals
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

关于为所有要实现的数字类型实现IArithmetic的讨论已经很多,但是由于效率不是多态构造,因此存在效率问题,因此,使CArithmetic约束可以解决该问题。


C#中的运算符是静态的,因此这仍然没有意义。
约翰·桑德斯

就是这一点,约束条件将验证TYPE(非实例)是否具有+运算符(并扩展为+ =运算符)并允许生成模板,然后在实际模板替换发生时,保证所使用的对象是具有该运算符(或其他静态方法)的类型。因此,您可以说:SumElements <int>(0,5); SumElements(int initVal,int [] values){foreach(值中的var v){initVal + = v; }},这当然是很有意义的
杰里米·索伦森

1
请记住,这不是模板。它是泛型,与C ++模板不同。
约翰·桑德斯

@JohnSaunders:泛型不是模板,我不知道如何合理地使它们与静态绑定的运算符一起使用,但是在泛型上下文中,在许多情况下,指定a T应该具有静态值可能很有用。成员(例如,生产T实例的工厂)。即使不更改运行时,我认为也可以定义约定和帮助程序方法,以允许语言以有效且可互操作的方式实现诸如语法糖之类的事情。如果每个带有虚拟静态方法的接口都有一个帮助器类……
supercat 2013年

1
@JohnSaunders:问题不在于该方法实现了一个接口,而是在于编译器在没有对象基础的情况下无法选择要调用的虚拟方法。一种解决方案是不使用虚拟分派(这将不起作用)来分派“静态接口”调用,而是使用对通用静态类的调用。基于类型的泛型调度不需要具有实例,而仅需要具有类型。
2013年


3

您似乎想要的允许通过Type或该类型的任何实例调用静态方法。这至少会导致模棱两可,这不是期望的特征。

关于它是否重要,最佳实践以及是否存在以一种或另一种方式执行的性能问题,将会有无休止的辩论。通过简单地不支持它,C#使我们不必担心它。

符合此要求的编译器也可能会失去一些优化,而这些优化可能来自实例方法与静态方法之间更严格的分隔。


有趣的是,通过VB.Net中的两个Type ad Instance都可以调用静态方法(即使在后一种情况下IDE也会发出警告)。看来没有问题。您可能对优化是正确的。
Kramii

3

您可以将类的静态方法和非静态方法视为不同的接口。调用时,静态方法解析为单例静态类对象,非静态方法解析为您处理的类的实例。因此,如果您在接口中使用静态和非静态方法,那么实际上我们确实希望使用接口来访问一个内聚的东西时,您将有效地声明两个接口。


这是一个有趣的POV,很可能是C#设计人员想到的。从现在开始,我将以不同的方式考虑静态成员。
Kramii

3

例如,我缺少接口方法的静态实现或Mark Brackett引入的所谓“类型方法”:

从数据库存储中读取数据时,我们有一个通用的DataTable类,该类处理从任何结构的表中读取数据。所有表的特定信息都放在每个表的一个类中,该类还保存数据库中一行的数据,并且必须实现IDataRow接口。IDataRow中包含要从数据库读取的表的结构的描述。在从DB读取数据之前,DataTable必须从IDataRow请求数据结构。当前看起来像:

interface IDataRow {
  string GetDataSTructre();  // How to read data from the DB
  void Read(IDBDataRow);     // How to populate this datarow from DB data
}

public class DataTable<T> : List<T> where T : IDataRow {

  public string GetDataStructure()
    // Desired: Static or Type method:
    // return (T.GetDataStructure());
    // Required: Instantiate a new class:
    return (new T().GetDataStructure());
  }

}

每个表读取一次只需要GetDataStructure,实例化一个以上实例的开销很小。但是,在这种情况下,这很好。


1

仅供参考:通过为接口创建扩展方法,您可以获得与所需行为类似的行为。扩展方法将是共享的,不可覆盖的静态行为。但是,不幸的是,这种静态方法不会成为合同的一部分。


1

接口是定义的可用功能的抽象集。

该接口中的方法是否表现为静态是应该隐藏在接口后面的实现细节。将接口方法定义为静态方法是错误的,因为您将不必要地强制以某种方式实现该方法。

如果将方法定义为静态,则实现该接口的类将不会像它那样被封装。封装是在面向对象设计中努力争取的一件好事(我不会讲解为什么,您可以在这里阅读:http : //en.wikipedia.org/wiki/object-oriented)。因此,接口中不允许使用静态方法。


1
是的,接口声明中的静态变量很愚蠢。不应强迫类以某种方式实现接口。但是,C#是否将类限制为使用非静态方法实现接口。这有意义吗?为什么?
Kramii

您不能使用关键字“静态”。但是没有限制,因为您不需要static关键字来编写静态行为的方法。只需编写一个不访问其对象的任何成员的方法,然后它将像静态方法一样工作。因此,存在限制。
Scott Langham

是True Scott,但是在代码的另一部分中,它不允许某人以静态方式访问该方法。我并不是没有想到您想要的原因,但这似乎是OP的要求。
克里斯·马拉斯蒂·乔治

好吧,如果您真的有需要,可以将其作为静态方法编写以在代码的另一部分中访问,然后编写非静态方法以调用静态方法。我可能是错的,但我怀疑这是发问者的目标。
Scott Langham

1

静态类应该能够做到这一点,以便可以通用使用它们。我不得不实现Singleton来实现所需的结果。

我有一堆静态业务层类,它们为每个实体类型(如“用户”,“团队”等)实现了CRUD方法,例如“创建”,“读取”,“更新”,“删除”。然后,我创建了一个基础控件,该控件具有实现CRUD方法的业务层类的抽象属性。这使我能够从基类中自动执行“创建”,“读取”,“更新”,“删除”操作。由于静态限制,我不得不使用Singleton。


1

大多数人似乎都忘记了,在OOP中,类也是对象,因此它们具有消息,由于某种原因,c#将其称为“静态方法”。实例对象和类对象之间存在差异的事实仅显示了该语言的缺陷或不足。虽然对C#持乐观态度...


2
问题在于,这个问题与“在OOP中”无关。关于“在C#中”。在C#中,没有“消息”。
约翰·桑德斯

1

好的,这是需要“类型方法”的示例。我正在基于某些源XML创建一组类之一。所以我有一个

  static public bool IsHandled(XElement xml)

在每个类上依次调用的函数。

该函数应该是静态的,否则我们会浪费时间创建不合适的对象。正如@Ian Boyde指出的那样,可以在工厂类中完成此操作,但这只会增加复杂性。

最好将其添加到接口以强制类实现者实现它。这不会造成很大的开销-这只是编译/链接时间检查,不会影响vtable。

但是,这也是一个相当小的改进。由于该方法是静态的,因此作为调用方的我必须显式调用它,因此,如果未实现,则会立即产生编译错误。允许在接口上进行指定将意味着该错误在开发周期的早期出现,但是与其他接口损坏问题相比,这是微不足道的。

因此,这是一个次要的潜在功能,总的来说最好将其遗漏。


1

Microsoft通过使用静态元素创建类的特殊实例,并在C#中实现了静态类的事实,这只是实现静态功能的一种奇怪方式。这不是理论上的观点。

接口应该是类接口的描述符,或者它是如何与之交互的,并且应该包括静态的交互。接口的一般定义(来自Meriam-Webster):不同事物相遇并相互交流或影响的地方或区域。当您完全忽略一个或多个静态类的静态组件时,我们将忽略这些坏男孩如何相互作用的大部分内容。

这是一个非常清晰的示例,说明可以在静态类中使用接口的地方非常有用:

public interface ICrudModel<T, Tk>
{
    Boolean Create(T obj);
    T Retrieve(Tk key);
    Boolean Update(T obj);
    Boolean Delete(T obj);
}

当前,我编写了包含这些方法的静态类,而没有进行任何检查以确保我没有忘记任何东西。就像在OOP之前糟糕的编程时代一样。


这是一个古老的问题;如今,我们很难避免基于观点的问题,因为它们很难权威地回答。回答此问题的唯一好方法是描述MS这样做的原因或可以证明的理由。
内森·塔吉

1

C#和CLR应该像Java一样在接口中支持静态方法。静态修饰符是合同定义的一部分,确实具有含义,特别是行为和返回值不会因实例而异,尽管它可能因调用而异。

就是说,我建议当您要在接口中使用静态方法而不能使用时,请改用注释。您将获得所需的功能。


0

我认为简短的答案是“因为它的用处为零”。要调用接口方法,您需要一个类型的实例。您可以从实例方法中调用所需的任何静态方法。


0

我认为问题在于,对于这种情况,C#需要另一个关键字。您需要一个方法,其返回值仅取决于调用它的类型。如果未知的类型,则不能将其称为“静态”。但是一旦知道类型,它将变为静态。这个想法是“未解决的静态”,它还不是静态的,但是一旦我们知道接收类型,它将是静态的。这是一个非常好的概念,这就是程序员不断要求它的原因。但这与设计者对语言的思考方式不太吻合。

由于它不可用,因此我采取了以下所示的方式使用非静态方法。并非完全理想,但是我看不到任何更有意义的方法,至少对我而言不是。

public interface IZeroWrapper<TNumber> {
  TNumber Zero {get;}
}

public class DoubleWrapper: IZeroWrapper<double> {
  public double Zero { get { return 0; } }
}

0

按照面向对象的概念,由类实现的接口并具有使用对象访问这些实现的函数(或方法)的契约。

因此,如果要访问接口合同方法,则必须创建对象。在使用静态方法的情况下,始终必须禁止这样做。静态类,方法和变量从不要求对象并在不创建该区域(或类)对象的情况下将其加载到内存中,或者可以说不需要对象创建。


0

从概念上讲,没有理由不能定义包含静态方法的协定。

对于当前的C#语言实现,该限制是由于允许继承基类和接口。如果“ class SomeBaseClass”实现“ interface ISomeInterface”并且“ class SomeDerivedClass:SomeBaseClass,ISomeInterface”也实现该接口,则用于实现接口方法的静态方法将无法编译,因为静态方法不能具有与实例方法相同的签名(存在于基类中以实现接口)。

静态类在功能上与单例相同,并且具有与语法更简洁的单例相同的目的。由于单例可以实现接口,因此静态实现接口在概念上是有效的。

因此,它可以简单地归结为C#名称冲突的局限性,例如在继承过程中实例和具有相同名称的静态方法。没有理由为什么不能“升级” C#以支持静态方法协定(接口)。


-1

当一个类实现一个接口时,它正在为接口成员创建实例。虽然静态类型没有实例,但在接口中具有静态签名毫无意义。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.