为什么C#(或.NET)不允许我们在接口中放置静态/共享方法?
似乎与here重复。但我的想法有点不同,我只想为我的插件(界面)提供帮助
不应该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<string, object> pkColumns);
ShowTaskNewDelegate ShowTaskNew { set; get; }
ShowTaskOpenDelegate ShowTaskOpen { set; get; }
// would not compile with this:
public static Dictionary<string, ITaskPlugin> GetPlugins(string directory)
{
var l = new Dictionary<string, ITaskPlugin>();
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<string, ITaskPlugin> GetPlugins(string directory)
{
var l = new Dictionary<string, ITaskPlugin>();
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
}
答案 0 :(得分:11)
接口的概念是代表合同,而不是实现。
我不记得IL是否实际 是否允许静态方法在接口中实现 - 我怀疑它确实存在 - 但这在某种程度上混淆了这个概念。
我可以看到你的观点 - 知道哪些辅助方法可用于与接口连接(并且扩展方法在那里特别相关)有时很有用但我个人希望将它们放在一个单独的类中,无论如何保持心理模型清洁。
答案 1 :(得分:6)
我已经多次遇到这种情况并做了一些研究。可悲的是,IL实际上支持这一点。我对此感到非常沮丧,我写了一篇关于它的博客文章。你可以找到它here。
答案 2 :(得分:2)
为了您的目的,将插件接口与插件加载器实现分离会更好:这将使您的设计更少耦合和更具凝聚力(从而降低复杂性)。
至于“界面中的静态方法”,请参阅this。
作为旁注:你真的不想发明另一个插件架构:看看MEF。
答案 3 :(得分:2)
查看我的博客条目,了解接口中实现的静态方法(对于无耻的自我引用感到抱歉)
[删除了断开的链接http:/ ...]
编辑: 我在下面粘贴了一个缓存版本:
[...]
使用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#不支持在接口上实现静态方法,并且只能通过反射调用C#。
我还没有使用其他IDE测试过,比如SharpDevelop ...所以还不知道它是如何处理这种情况的。
答案 4 :(得分:1)
接口只是一个接口。它并不意味着用于描述行为。当一个类实现一个接口时,该类只是说“我保证我提供带有这些签名的方法/事件/等”。
你想要的是没有静态方法的接口和实现接口和静态方法的抽象基类。然后其他类可以从基类继承并更改接口的方法实现。但即使这是一个值得怀疑的设计。
答案 5 :(得分:0)
静态方法与声明它们的类型相关联,与覆盖无关。如果您能够将静态方法附加到接口,则必须通过接口本身引用它,例如ITaskPlugin.GetPlugins(...)
您要做的是:
1)将您的方法放在抽象基类中,因为接口不是为了保存实现代码而设计的,或者
2)创建一个适用于该接口的扩展方法,然后您就可以访问它而无需使用基类。
答案 6 :(得分:0)
接口的目的是声明一个对象的接口,通过它可以访问它。由于这是它的唯一目的,因此将代码放在接口中是没有意义的。 如果您仍想在界面中添加一些代码,可以使用扩展方法。