为啥 C# 不允许静态方法实现接口?
Posted
技术标签:
【中文标题】为啥 C# 不允许静态方法实现接口?【英文标题】:Why Doesn't C# Allow Static Methods to Implement an Interface?为什么 C# 不允许静态方法实现接口? 【发布时间】:2010-09-20 12:24:00 【问题描述】:为什么 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;
....
【问题讨论】:
好吧,Java 8 拥有它 (***.com/questions/23148471/…)。 了解如何将静态行为与继承或接口实现结合起来:***.com/a/13567309/880990IListItem.ScreenName() => ScreenName()
(使用 C# 7 语法)将通过调用静态方法显式实现接口方法。但是,当您向其中添加继承时,事情会变得很糟糕(您必须重新实现接口)
只是让大家知道等待现在结束了! C# 8.0 具有静态接口方法:dotnetfiddle.net/Lrzy6y(尽管它们的工作方式与 OP 希望它们的工作方式略有不同——您不必实现它们)
【参考方案1】:
因为接口是继承结构,静态方法不好继承。
【讨论】:
【参考方案2】:我猜是近视。
最初设计时,接口仅用于类的实例
IMyInterface val = GetObjectImplementingIMyInterface();
val.SomeThingDefinedinInterface();
只有在引入接口作为泛型的约束之后,向接口添加静态方法才有实际用途。
(回应评论:)我认为现在更改它需要更改 CLR,这将导致与现有程序集不兼容。
【讨论】:
我第一次遇到问题是在泛型的上下文中,但我想知道在接口中包含静态方法在其他上下文中是否也有用?有什么不能改变的原因吗? 我在实现需要参数类型以使用某些参数创建自身的泛型类时也遇到了这种情况。由于 new() 不能带任何东西。 Kramii,你想好怎么做了吗? @Kramii:静态 API 合约。我不想要一个对象实例,只是一个特定签名的保证,例如。 IMatrixMultiplier 或 ICustomSerializer。作为类成员的 Funcs/Actions/Delegates 可以解决问题,但在 IMO 中,这有时看起来有点矫枉过正,对于尝试扩展 API 的没有经验的人来说可能会感到困惑。【参考方案3】:接口指定对象的行为。
静态方法不指定对象的行为,而是以某种方式影响对象的行为。
【讨论】:
对不起.. 我不确定这是对的!接口不指定行为。接口定义了一组命名操作。两个类可以实现一个接口的方法,以完全不同的方式表现。因此,接口根本不指定行为。实现它的类。 希望你不要认为我很挑剔.. 但我认为对于任何学习 OO 的人来说,这是一个重要的区别。 一个接口应该指定一个包含行为和表示的合约。这就是为什么改变接口调用的行为是禁忌的,因为两者都应该被修复。如果您有一个调用行为不同的接口(即 IList.Add 进行了删除),那就不对了。 嗯,是的,你会在头脑中扭曲来定义一个与其名称不相符的方法。如果你有 IAlertService.GetAssistance(),它的行为可能是闪光、发出警报或用棍子戳眼睛。 实现也可以写入日志文件。此行为未在界面中指定。但是,也许你是对的。真正应该尊重“寻求帮助”的行为。【参考方案4】:假设你问为什么你不能这样做:
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,并把它作为一个指示它可能这不是一个好主意。
【讨论】:
一个完美的例子!但我不确定我是否理解你的推理。当然,编译器可以也被设计为查看 statuc 成员吗?实例没有用于实现 theis 方法的地址表吗?静态方法不能包含在这个表中吗? 在某些情况下这可能很有用。例如,我希望所有实现者都实现一个 GetInstance 方法,该方法采用 XElement 参数。我既不能将其定义为接口中的静态方法,也不能要求接口的构造函数签名。 很多人都在权衡双方:从“这毫无意义”到“这是一个错误,我希望你能做到”。 (我认为它有有效的用例,这就是我在这里结束的方式。)作为一种解决方法,实例方法可以简单地委托给静态方法。 您也可以简单地将片段作为扩展方法实现到基接口上,如:public static object MethodName(this IBaseClass base)
在静态类中。然而,缺点是与接口继承不同 - 这不会强制/允许各个继承者很好地覆盖方法。
使用泛型会很有意义。例如 void Something您似乎想要的是允许通过 Type 或该类型的任何实例调用静态方法。这至少会导致模棱两可,这不是一个理想的特征。
关于它是否重要、哪种是最佳做法以及以这种或另一种方式执行是否存在性能问题,将会有无休止的争论。通过简单地不支持它,C# 让我们不必担心它。
符合这种愿望的编译器也可能会失去一些优化,这些优化可能伴随着实例和静态方法之间更严格的分离。
【讨论】:
有趣的是,在 VB.Net 中通过 Type 和 Instance 都可以调用静态方法(即使 IDE 在后一种情况下会发出警告)。这似乎不是问题。您可能对优化是正确的。【参考方案6】:接口是定义的可用功能的抽象集合。
该接口中的方法是否表现为静态是应该隐藏在接口后面的实现细节。将接口方法定义为静态是错误的,因为您会不必要地强制该方法以某种方式实现。
如果方法被定义为静态的,那么实现接口的类就不会被尽可能地封装。在面向对象的设计中,封装是一件好事(我不会解释为什么,你可以在这里阅读:http://en.wikipedia.org/wiki/Object-oriented)。因此,接口中不允许使用静态方法。
【讨论】:
是的,接口声明中的静态是愚蠢的。不应强制类以某种方式实现接口。但是,C# 是否将类限制为使用非静态方法实现接口。这有意义吗?为什么? 您不能使用关键字“静态”。但是没有限制,因为您不需要 static 关键字来编写静态行为的方法。只需编写一个不访问其任何对象成员的方法,它就会像静态方法一样运行。所以,有限制。 True Scott,但它不允许某人在代码的另一部分以静态方式访问该方法。并不是说我能想到你想要的原因,但这似乎是 OP 所要求的。 好吧,如果你真的觉得有必要,你可以把它写成一个静态方法来访问代码的另一部分,而只写非静态方法来调用静态方法。我可能是错的,但我怀疑这是提问者的目标。【参考方案7】:仅供参考:您可以通过为接口创建扩展方法来获得与您想要的类似的行为。扩展方法将是一个共享的、不可覆盖的静态行为。然而,不幸的是,这个静态方法不会成为合约的一部分。
【讨论】:
【参考方案8】:您可以将一个类的静态方法和非静态方法视为不同的接口。调用时,静态方法解析为单例静态类对象,非静态方法解析为您处理的类的实例。因此,如果您在接口中使用静态和非静态方法,那么当我们真的希望接口用于访问一个内聚的事物时,您实际上是在声明两个接口。
【讨论】:
这是一个有趣的 POV,并且可能是 C# 设计者想到的。从现在开始,我会以不同的方式考虑静态成员。【参考方案9】:我认为简短的回答是“因为它的用处为零”。 要调用接口方法,您需要该类型的实例。从实例方法中,您可以调用任何您想要的静态方法。
【讨论】:
【参考方案10】:我的(简化的)技术原因是静态方法不在vtable 中,并且在编译时选择了调用站点。这与您不能拥有覆盖或虚拟静态成员的原因相同。有关更多详细信息,您需要 CS 毕业生或编译器专家——我都不是。
出于政治原因,我会quote Eric Lippert(他是编译器专家,拥有滑铁卢大学的数学、计算机科学和应用数学学士学位(来源:LinkedIn):
...静态方法的核心设计原则,给它们命名的原则...[是]...它总是可以准确地确定,在编译时,将调用什么方法。也就是说,该方法可以仅通过对代码的静态分析来解决。
请注意,Lippert 确实为所谓的类型方法留出了空间:
也就是说,一种与类型关联的方法(如静态),它不采用不可为空的“this”参数(与实例或虚拟不同),但调用的方法将取决于构造的类型T 的(不像静态的,它必须在编译时确定)。
但尚未确信其有用性。
【讨论】:
太好了,这就是我想写的答案——我只是不知道实现细节。 很好的答案。我想要这种“类型方法”!在很多情况下都会很有用(想想类型/类的元数据)。 这是正确答案。您将接口传递给某人,他们需要知道如何调用方法。接口只是一个虚拟方法表。你的静态类没有那个。调用者不知道如何调用方法。 (在我阅读这个答案之前,我认为 C# 只是迂腐。现在我意识到这是一个技术限制,由 what an interface is 强加)。其他人会和你谈论这是一个糟糕的设计。这不是一个糟糕的设计 - 这是一个技术限制。 完全可以为带有关联 vtable 的静态类生成对象。看看 Scala 如何处理object
s 以及它们是如何被允许实现接口的。
vtable 可以只指向静态实现,就像Func<>
所做的那样,但它不仅包含一个指针,还包含接口所需方法的所有指针。在静态调度中使用static
会为了“面向对象”而人为地限制语言。【参考方案11】:
因为接口的目的是允许多态性,能够传递已定义的任意数量的已定义类的实例以实现已定义的接口...保证在您的多态调用中,代码将是能够找到您正在调用的方法。允许静态方法实现接口是没有意义的,
你会怎么称呼它?
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;
【讨论】:
其他语言(例如 Java)允许从对象实例调用静态方法,尽管您会收到一条警告,提示您应该从静态上下文中调用它们。 在 .Net 中也允许从实例调用静态方法。那是另一回事。如果静态方法实现了接口,则可以在没有实例的情况下调用它。那是没有意义的。请记住,您不能将实现放在接口中,它必须在类中。因此,如果定义了五个不同的类来实现该接口,并且每个类都有这个静态方法的不同实现,那么编译器会使用哪一个呢? @CharlesBretana 在泛型的情况下,用于传入的类型。我看到为静态方法提供接口非常有用(或者,如果你愿意,我们称它们为“静态接口”和允许一个类同时定义静态接口和实例接口)。因此,如果我有Whatever<T>() where T:IMyStaticInterface
,我可以调用Whatever<MyClass>()
,并在Whatever<T>()
实现中拥有T.MyStaticMethod()
,而无需实例。要调用的方法将在运行时确定。你可以通过反射来做到这一点,但没有强制的“合同”。
术语“多态性”比仅仅通过 vtable 调度 virtcall 具有更通用的含义。甚至函数重载也是一种多态性,例如 C++ 中的模板元编程允许以 C# 中不可能的方式使用它(即使仍在静态分派时也可以这样做)。将所有int.TryParse
、short.TryParse
等方法组合到泛型IParse.TryParse
可以允许在泛型类型T where T : IParse
上调用T.TryParse
。在没有骇客的情况下在 C# 中是不可能的,但在 C++(和其他一些语言)中可以实现等价的东西。【参考方案12】:
举个例子,我缺少接口方法的静态实现或 Mark Brackett 引入的“所谓的类型方法”:
从数据库存储中读取时,我们有一个通用的 DataTable 类来处理从任何结构的表中读取。所有表特定信息都放在每个表的一个类中,该类还保存数据库中一行的数据,并且必须实现 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 只需要读取每个表一次,实例化一个更多实例的开销是最小的。但是,在这种情况下会很好。
【讨论】:
【参考方案13】:静态类应该能够做到这一点,因此它们可以被普遍使用。我不得不实现一个 Singleton 来实现预期的结果。
我有一堆静态业务层类,它们为每个实体类型(如“用户”、“团队”等)实现了“创建”、“读取”、“更新”、“删除”等 CRUD 方法。然后我为实现 CRUD 方法的业务层类创建了一个具有抽象属性的基本控件。这使我能够自动化基类中的“创建”、“读取”、“更新”、“删除”操作。由于静态限制,我不得不使用单例。
【讨论】:
【参考方案14】:这里的大多数答案似乎都没有抓住重点。多态不仅可以在实例之间使用,也可以在类型之间使用。当我们使用泛型时,这通常是需要的。
假设我们在泛型方法中有类型参数,我们需要对其进行一些操作。我们不想实例化,因为我们不知道构造函数。
例如:
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...
这显然是丑陋的,也不必要地使所有其他方法的代码复杂化。显然,这也不是一个优雅的解决方案!
【讨论】:
+1 表示“这里的大多数答案似乎都没有抓住重点。”;令人难以置信的是,几乎所有答案似乎都避开了问题的核心,而是深入研究了大多数无用的措辞...... @Chris 这是一个让我再次遇到此限制的具体示例。我想向类添加一个 IResettable 接口,以指示它们将某些数据缓存在可以由站点管理员重置的静态变量中(例如,订单类别列表、一组增值税率、从外部 API 检索的类别列表)为了减少数据库和外部 API 的命中,这显然会使用静态方法重置。这使我可以自动检测可以重置哪些类。我仍然可以这样做,但是该方法没有强制执行或在 IDE 中自动添加,并且依赖于希望。 @Chris 我不同意,大量的矫枉过正。当更多的架构是“最佳”解决方案时,这总是语言缺陷的标志。还记得自从 C# 有了泛型和匿名方法后,就再也没有人谈论的所有模式了吗? 你不能使用 "where T : IQueryable, T : IDoSomeStaticMath" 或类似的吗? @ChrisMarasti-Georg:一个有点脏但有趣的方法是这个小结构:public abstract class DBObject<T> where T : DBObject<T>, new()
,然后让所有 DB 类继承为DBObject<T>
。然后您可以在抽象超类中使按键检索成为具有返回类型 T 的静态函数,使该函数创建 T 的新对象,然后在其上调用受保护的String GetRetrieveByKeyQuery()
(在超类上定义为抽象)对象以获取要执行的实际查询。虽然这可能有点离题【参考方案15】:
我知道这是一个老问题,但它很有趣。这个例子不是最好的。我认为如果你展示一个用例会更清楚:
字符串 DoSomething仅仅能够拥有静态方法实现一个接口并不能实现你想要的;所需要的是将静态成员作为接口的part。我当然可以想象很多用例,尤其是在能够创造事物的时候。我可以提供两种可能会有所帮助的方法:
-
创建一个静态泛型类,其类型参数将是您要传递给上述 DoSomething 的类型。此类的每个变体都会有一个或多个静态成员持有与该类型相关的内容。可以通过让每个感兴趣的类调用“注册信息”例程来提供此信息,或者通过在运行类变体的静态构造函数时使用反射来获取信息。我相信后一种方法被诸如 Comparer
这些方法都没有真正吸引人的地方。另一方面,我希望如果 CLR 中存在干净地提供这种功能的机制,.net 将允许指定参数化的“新”约束(因为知道一个类是否具有具有特定签名的构造函数似乎很难与知道它是否具有具有特定签名的静态方法相比)。
【讨论】:
【参考方案16】:就接口代表“契约”的程度而言,静态类实现接口似乎是合理的。
上述论点似乎都忽略了关于合同的这一点。
【讨论】:
我完全同意这个简单但有效的答案。 “静态接口”的有趣之处在于它代表了一个合同。也许它不应该被称为“静态接口”,但我们仍然错过了一个构造。例如,查看 .NET 关于 ICustomMarshaler 接口的官方文档。它要求实现它的类“添加一个名为 GetInstance 的静态方法,该方法接受 String 作为参数并具有 ICustomMarshaler 返回类型”。这确实看起来像简单英语中的“静态接口”定义,而我更喜欢用 C#... @SimonMourier 该文档本来可以写得更清楚,但您误解了它。需要 GetInstance 静态方法的不是 ICustomMarshaler 接口。正是 [MarshalAs] 代码属性需要这个。他们使用工厂模式来允许属性获取附加封送器的实例。不幸的是,他们完全忘记了在 MarshalAs 文档页面上记录 GetInstance 要求(它仅显示使用内置编组实现的示例)。 @ScottGartner - 不要明白你的意思。 msdn.microsoft.com/en-us/library/… 明确指出:“除了实现 ICustomMarshaler 接口之外,自定义封送处理程序还必须实现一个名为 GetInstance 的静态方法,该方法接受一个字符串作为参数并具有 ICustomMarshaler 的返回类型。此静态方法由公共语言运行时的 COM 调用互操作层以实例化自定义封送拆收器的实例。”。这绝对是一个静态合同定义。【参考方案17】:大多数人似乎忘记了在 OOP 中,类也是对象,因此它们有消息,出于某种原因,c# 将其称为“静态方法”。 实例对象和类对象之间存在差异这一事实仅表明该语言存在缺陷或缺点。 虽然对 c# 持乐观态度...
【讨论】:
问题是这个问题与“in OOP”无关。这是关于“在 C# 中”。在 C# 中,没有“消息”。【参考方案18】:好的,这里是需要“类型方法”的示例。我正在创建基于某些源 XML 的一组类中的一个。所以我有一个
static public bool IsHandled(XElement xml)
每个类依次调用的函数。
函数应该是静态的,否则我们会浪费时间创建不合适的对象。 正如@Ian Boyde 指出的那样,它可以在工厂类中完成,但这只会增加复杂性。
将它添加到接口以强制类实现者实现它会很好。这不会导致显着的开销 - 它只是编译/链接时间检查,不会影响 vtable。
但是,这也是一个相当小的改进。由于该方法是静态的,因此我作为调用者必须显式调用它,因此如果未实现,则会立即出现编译错误。允许在接口上指定它意味着此错误在开发周期的早期出现,但与其他损坏的接口问题相比,这是微不足道的。
所以这是一个潜在的小功能,总的来说最好不要考虑。
【讨论】:
【参考方案19】:关于在非泛型上下文中使用的静态方法,我同意在接口中允许它们没有多大意义,因为如果你有对接口的引用,你将无法调用它们。然而,在语言设计中存在一个基本漏洞,它不是在多态上下文中而是在通用上下文中使用接口创建的。在这种情况下,接口根本不是接口,而是约束。因为 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(不是实例)具有 + 运算符(并扩展为 += 运算符)并允许生成模板,然后在发生实际模板替换时所使用的对象保证是具有该运算符(或其他静态方法)的类型。所以你可以说: SumElementsT
应该有一个static 成员(例如,产生T
实例的工厂)。即使没有运行时更改,我认为也可以定义约定和辅助方法,允许语言以高效和可互操作的方式实现语法糖之类的东西。如果每个带有虚拟静态方法的接口都有一个帮助类...
@JohnSaunders:问题不在于该方法实现了一个接口,而是编译器无法选择一个虚拟方法来调用,而没有一个对象实例作为选择的基础.一种解决方案是不使用虚拟调度(这不起作用)而是使用对通用静态类的调用来调度“静态接口”调用。基于类型的泛型调度不需要有实例,而只需要有一个类型。【参考方案20】:
我认为问题在于 C# 需要另一个关键字,正是这种情况。您需要一个返回值仅取决于调用它的类型的方法。如果所述类型未知,则不能将其称为“静态”。但是一旦类型变得已知,它就会变成静态的。 “未解决的静态”是一个想法——它还不是静态的,但是一旦我们知道接收类型,它就会是。这是一个非常好的概念,这就是程序员不断要求它的原因。但它并不完全符合设计师对语言的看法。
由于它不可用,我已采用如下所示的方式使用非静态方法。不完全理想,但我看不出任何更有意义的方法,至少对我来说不是。
public interface IZeroWrapper<TNumber>
TNumber Zero get;
public class DoubleWrapper: IZeroWrapper<double>
public double Zero get return 0;
【讨论】:
【参考方案21】:静态类是由微软在 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 有或明显应该有的原因,以他们这样做的方式。【参考方案22】:C# 和 CLR 应该像 Java 一样支持接口中的静态方法。 static 修饰符是合约定义的一部分,并且确实有意义,特别是行为和返回值不会因实例而异,尽管它可能仍因调用而异。
也就是说,我建议当您想在接口中使用静态方法而无法使用时,请改用注解。您将获得所需的功能。
【讨论】:
【参考方案23】:根据面向对象的概念接口由类和实现 有合同访问这些实现的功能(或方法)使用 对象。
因此,如果您想访问接口契约方法,您必须创建对象。在静态方法的情况下,总是不允许的。静态类、方法和变量永远不需要对象并在不创建该区域(或类)的对象的情况下加载到内存中,或者您可以说不需要创建对象。
【讨论】:
【参考方案24】:当一个类实现一个接口时,它正在为接口成员创建实例。虽然静态类型没有实例,但在接口中使用静态签名是没有意义的。
【讨论】:
【参考方案25】:从概念上讲接口没有理由不能定义包含静态方法的协定。
对于当前的 C# 语言实现,限制是由于允许继承基类和接口。如果“class SomeBaseClass”实现了“interface ISomeInterface”并且“class SomeDerivedClass : SomeBaseClass, ISomeInterface”也实现了该接口,实现接口方法的静态方法将无法编译,因为静态方法不能具有与实例方法相同的签名(这将存在于基类中以实现接口)。
静态类在功能上与单例相同,其用途与语法更简洁的单例相同。由于单例可以实现接口,因此静态接口实现在概念上是有效的。
所以它简单地归结为 C# 名称冲突的限制,例如跨继承的同名静态方法。没有理由不能“升级”C# 以支持静态方法协定(接口)。
【讨论】:
【参考方案26】:从 c# 9 开始允许接口中的静态方法(请参阅https://www.dotnetcurry.com/csharp/simpler-code-with-csharp-9)。
【讨论】:
以上是关于为啥 C# 不允许静态方法实现接口?的主要内容,如果未能解决你的问题,请参考以下文章