为什么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;
}


....


}
196251 次浏览

因为接口是继承结构,静态方法继承不好。

我猜这是短视。

在最初设计时,接口仅用于类的实例

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

只有引入接口作为泛型的约束,向接口中添加静态方法才有实际用途。

(回复评论:)我认为现在更改它需要更改CLR,这将导致与现有程序集不兼容。

接口指定对象的行为。

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

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

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,并将其作为它可能不是一个好主意的指示。

您想要的是允许通过Type或该类型的任何实例调用静态方法。这至少会导致歧义,这不是一个理想的特征。

关于这是否重要,这是最佳实践,以及这样做是否存在性能问题,将会有无休止的争论。通过简单地不支持它,c#使我们不必担心它。

遵循这一愿望的编译器也可能会失去一些优化,而这些优化可能会带来实例方法和静态方法之间更严格的分离。

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

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

如果方法被定义为静态的,那么实现接口的类就不会像它应该的那样被封装。在面向对象设计中,封装是一件值得努力的事情(我不会详细说明原因,你可以在这里阅读:http://en.wikipedia.org/wiki/Object-oriented)。因此,在接口中不允许使用静态方法。

供您参考:您可以通过为接口创建扩展方法来获得与您想要的类似的行为。扩展方法将是一个共享的、不可覆盖的静态行为。然而,不幸的是,这个静态方法不是契约的一部分。

您可以将类的静态方法和非静态方法看作是不同的接口。调用时,静态方法解析为单例静态类对象,而非静态方法解析为所处理的类的实例。所以,如果你在一个接口中使用静态和非静态方法,你实际上是在声明两个接口,而实际上我们想要接口被用来访问一个内聚的东西。

我认为简短的答案是“因为它毫无用处”。 要调用接口方法,需要该类型的实例。从实例方法中,你可以调用任何你想要的静态方法

我的(简化的)技术原因是静态方法不在虚表中,并且调用地点是在编译时选择的。这和你不能有覆盖或虚拟静态成员的原因是一样的。要了解更多细节,你需要一个计算机专业毕业生或编译专家——而我两者都不是。

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

...静态方法的核心设计原则,给它们命名的原则是…在编译时,总是可以准确地确定将调用什么方法。也就是说,可以仅通过对代码的静态分析来解析该方法。

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

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

但还没有被它的实用性所说服。

因为接口的目的是允许多态性,能够传递任意数量的已定义类的实例,这些类都已定义,以实现已定义的接口……确保在多态调用中,代码能够找到您正在调用的方法。允许静态方法实现接口是没有意义的,

你怎么称呼它??


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;

举一个例子,我既没有接口方法的静态实现,也没有Mark Brackett介绍的“所谓的类型方法”:

当从数据库存储中读取数据时,我们有一个通用的DataTable类来处理从任何结构的表中读取数据。所有特定于表的信息都放在每个表的一个类中,每个表还保存来自DB的一行的数据,并且必须实现IDataRow接口。IDataRow中包含了要从数据库中读取的表的结构描述。DataTable在从DB中读取数据之前必须向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,实例化一个实例的开销是最小的。但是,在这种情况下它会很好。

静态类应该能够做到这一点,这样它们就可以被通用地使用。我不得不实现一个Singleton来实现预期的结果。

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

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

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

例如:

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>()

不幸的是,我只能想出“丑”;选择:

  • < p > < >强使用反射 丑陋,打败了接口和多态性的思想
  • < p > 创建完全独立的工厂类

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

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

    即使我们控制类的源,用作泛型参数,这也很难实现。原因是,例如,我们可能需要实例只在known中,“连接到db”。状态。

例子:

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

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

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

string DoSomething<T>() where T:ISomeFunction
{
if (T.someFunction())
...
}

仅仅能够有静态方法实现接口将无法实现你想要的;所需要的是有静态成员作为接口的部分。我当然可以想象出许多用例,特别是当它能够创建东西的时候。我可以提供两种可能有用的方法:

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

这些方法都不太吸引人。另一方面,我希望如果CLR中存在能够干净地提供这类功能的机制,.net将允许指定参数化的“new”约束(因为知道一个类是否具有具有特定签名的构造函数似乎与知道它是否具有具有特定签名的静态方法在难度上相当)。

在接口代表“契约”的程度上,静态类实现接口似乎是非常合理的。

上述论点似乎都忽略了合同的这一点。

大多数人似乎忘记了在面向对象编程中,类也是对象,所以它们有消息,由于某种原因,c#将其称为“静态方法”。 实例对象和类对象之间存在差异的事实只显示了语言中的缺陷或缺点。 不过我对c#很乐观

这里有一个需要类型方法的例子。我正在创建一组基于源XML的类之一。所以我有一个

  static public bool IsHandled(XElement xml)

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

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

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

然而,这也将是一个相当小的改进。由于方法是静态的,我作为调用者,必须显式地调用它,因此如果它没有实现,就会立即得到编译错误。允许在接口上指定它将意味着这个错误在开发周期中稍微提前出现,但与其他损坏的接口问题相比,这是微不足道的。

所以这是一个次要的潜在特性,总的来说最好还是忽略不计。

关于在非泛型上下文中使用的静态方法,我同意在接口中允许它们没有太大意义,因为如果你有一个接口引用,你将无法调用它们。然而,在语言设计中存在一个基本的漏洞,即不是在多态上下文中使用接口,而是在通用上下文中使用接口。在这种情况下,接口根本就不是一个接口,而是一个约束。因为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;
}
}

已经有很多关于为所有数字类型创建i算术来实现的讨论,但是存在效率问题,因为约束不是多态构造,所以创建算术约束可以解决这个问题。

我认为问题在于,c#需要另一个关键字来处理这种情况。您希望方法的返回值只取决于调用它的类型。如果所述类型未知,则不能称其为“静态”。但是一旦类型成为已知,它就会变成静态的。“未解决的静态”是这个想法——它还不是静态的,但一旦我们知道了接收类型,它就会是静态的。这是一个非常好的概念,这就是为什么程序员一直要求它。但它并不完全符合设计师对语言的想法。

因为它不可用,所以我采用如下所示的方式使用非静态方法。虽然不太理想,但至少对我来说,我找不到比这更有意义的方法了。

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


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

一个静态类是用c#实现的,微软用静态元素创建了一个特殊的类实例,这只是实现静态功能的一个奇怪之处。这不是一个理论观点。

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

下面是一个非常清晰的例子,说明了在静态类中使用接口是非常有用的:

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

目前,我在编写包含这些方法的静态类时没有进行任何检查,以确保我没有忘记任何东西。就像在面向对象编程之前的糟糕的编程时代。

c#和CLR应该像Java一样支持接口中的静态方法。静态修饰符是契约定义的一部分,确实有意义,具体来说,行为和返回值不会基于实例而变化,尽管在不同调用之间仍然可能不同。

也就是说,当您想在接口中使用静态方法而又不能使用时,我建议您使用注释。您将得到您正在寻找的功能。

As per Object oriented concept由类和实现的接口 有合同访问这些实现的功能(或方法)使用 对象。< / p >

如果你想访问接口契约方法,你必须创建对象。对于静态方法,这是不允许的。静态类、方法和变量永远不需要对象和加载到内存中,而不需要创建该区域(或类)的对象,或者你可以说不需要对象创建。

当类实现接口时,它是在为接口成员创建实例。虽然静态类型没有实例,但在接口中使用静态签名是没有意义的。

接口没有理由不能定义包含静态方法的契约。

对于当前的c#语言实现,限制是由于允许继承基类和接口。如果“类SomeBaseClass”实现了“接口ISomeInterface”和“类SomeDerivedClass: SomeBaseClass, ISomeInterface”也实现了接口,实现接口方法的静态方法将会编译失败,因为静态方法不能与实例方法具有相同的签名(实例方法将出现在实现接口的基类中)。

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

因此,这可以简单地归结为c#的实例名称冲突和跨继承的同名静态方法的限制。c#没有理由不能“升级”以支持静态方法契约(接口)。

从c# 9开始,接口中的静态方法是允许的(参见https://www.dotnetcurry.com/csharp/simpler-code-with-csharp-9)。

事实上,确实如此。

截至2022年年中,当前版本的c#完全支持所谓的static abstract成员:

interface INumber<T>
{
static abstract T Zero { get; }
}


struct Fraction : INumber<Fraction>
{
public static Fraction Zero { get; } = new Fraction();


public long Numerator;
public ulong Denominator;


....
}

请注意,根据你的Visual Studio版本和你安装的。net SDK,你必须至少更新其中一个(或两个),或者你必须启用预览功能(参见使用预览功能&Visual Studio中的预览语言)。

看到更多:

接口是一个OOPS概念,这意味着接口的每个成员都应该通过对象或实例得到使用。因此,接口不能有静态方法。