为什么C#(或.NET)不允许我们在接口中放置静态/共享方法?

为什么C#(或.NET)不允许我们在接口中放置静态/共享方法?

看似从这里重复。 但我的想法有点不同,我只想为我的插件(接口)添加一个帮助器

不应该C#至少允许这个想法吗?

namespace MycComponent { public interface ITaskPlugin : ITaskInfo { string Description { get; } string MenuTree { get; } string MenuCaption { get; } void ShowTask(Form parentForm); void ShowTask(Form parentForm, Dictionary pkColumns); ShowTaskNewDelegate ShowTaskNew { set; get; } ShowTaskOpenDelegate ShowTaskOpen { set; get; } // would not compile with this: public static Dictionary GetPlugins(string directory) { var l = new Dictionary(); foreach (string file in Directory.GetFiles(directory)) { var fileInfo = new FileInfo(file); if (fileInfo.Extension.Equals(".dll")) { Assembly asm = Assembly.LoadFile(file); foreach (Type asmType in asm.GetTypes()) { if (asmType.GetInterface("MycComponent.ITaskPlugin") != null) { var plugIn = (ITaskPlugin)Activator.CreateInstance(asmType); l.Add(plugIn.TaskName, plugIn); } } } } return l; } // GetPlugins. would not compile inside an interface } /* because of the error above, I am compelled to put the helper method in a new class. a bit overkill when the method should be closely coupled to what it is implementing */ public static class ITaskPluginHelper { public static Dictionary GetPlugins(string directory) { var l = new Dictionary(); foreach (string file in Directory.GetFiles(directory)) { var fileInfo = new FileInfo(file); if (fileInfo.Extension.Equals(".dll")) { Assembly asm = Assembly.LoadFile(file); foreach (Type asmType in asm.GetTypes()) { if (asmType.GetInterface("MycComponent.ITaskPlugin") != null) { var plugIn = (ITaskPlugin)Activator.CreateInstance(asmType); l.Add(plugIn.TaskName, plugIn); } } } } return l; } // GetPlugins } // ITaskPluginHelper } 

接口的概念是代表合同,而不是实现。

我不记得IL是否实际上允许静态方法在接口中实现 – 我对它有一种偷偷摸摸的怀疑 – 但这有点混淆了这个概念。

我可以看到你的观点 – 知道哪些辅助方法可用于与接口连接(和扩展方法在那里特别相关)有时很有用但我个人想要将它们放在一个单独的类中,只是为了保持心理模特干净。

我已经多次遇到这种情况并做了一些研究。 可悲的是,IL实际上支持这一点。 我对此感到非常沮丧,我写了一篇关于它的博客文章。 你可以在这里找到它。

为了您的目的,将插件接口与插件加载器实现分离会更好:这将使您的设计更少耦合和更具凝聚力(从而降低复杂性)。

至于“界面中的静态方法”,请参阅此内容 。

作为旁注:你真的不想发明另一个插件架构:看看MEF 。

查看关于接口中实现的静态方法的博客条目(对于无耻的自我引用感到抱歉)

[删除了断开的链接http:/ …]

dotnetjunkies网站被totaldevpro戳了…所以google缓存版本是唯一可用的版本

编辑:我在下面粘贴了一个缓存版本,我发现:

[…]

使用ILAsm编译以下内容:

 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) .ver 2:0:0:0 } .assembly MaLio.StaticInterface{ .hash algorithm 0x00008004 .ver 0:1:0:0 } .module MaLio.StaticInterface.dll .imagebase 0x00400000 .file alignment 0x00001000 .stackreserve 0x00100000 .subsystem 0x0003 .corflags 0x00000001 .class interface public abstract auto ansi MaLio.IMyInterface { .method public hidebysig newslot abstract virtual instance void DoInstanceWork() cil managed { } .method public hidebysig static void DoStaticWork() cil managed { ldstr "Static" call void [mscorlib]System.Console::WriteLine(string) ret } } .class public auto ansi beforefieldinit MaLio.MyClass extends [mscorlib]System.Object implements MaLio.IMyInterface { .method public hidebysig newslot virtual final instance void DoInstanceWork() cil managed { ldstr "Instance" call void [mscorlib]System.Console::WriteLine(string) ret } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ldarg.0 call instance void [mscorlib]System.Object::.ctor() ret } } 

然后可以调用此代码

 System.Type myInterface = typeof(MaLio.IMyInterface); // show that we really are dealing with an interface if (myInterface.IsInterface) { System.Reflection.MethodInfo staticMethod = myInterface.GetMethod("DoStaticWork"); staticMethod.Invoke(null, null); } 

智能感知(VS)在这里不能按预期工作。 它将静态方法识别为接口的实例方法,并且代码(如果遵循智能感知提示)看起来就像它将要编译一样。 C#编译器(MS C#)不编译代码,因为C#不支持在接口上实现静态方法,并且只能通过reflection调用C#。

我还没有测试过其他IDE,比如SharpDevelop ……所以还不知道它是如何处理这种情况的。

接口只是一个接口。 它并不意味着用于描述行为。 当一个类实现一个接口时,该类只是说“我保证我提供带有这些签名的方法/事件/等”。

你想要的是没有静态方法的接口和实现接口和静态方法的抽象基类。 然后其他类可以从基类inheritance并更改接口的方法实现。 但即使这是一个值得怀疑的设计。

静态方法与声明它们的类型相关联,与覆盖无关。 如果您能够将静态方法附加到接口,则必须通过接口本身引用它,例如ITaskPlugin.GetPlugins(...)

你想要做的是:

1)将您的方法放在抽象基类中,因为接口不是为了保存实现代码而设计的,或者

2)创建一个适用于接口的扩展方法,然后您就可以访问它而无需使用基类。

接口的目的是声明一个对象的接口,通过它可以访问它。 由于这是它的唯一目的,因此将代码放在接口中是没有意义的。 如果您仍想在界面中添加一些代码,则可以使用扩展方法。