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

Why shouldn't C#(or .NET) allow us to put a static/shared method inside an interface?

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

从这里看起来是重复的。但是我的想法有点不同,我只想为我的插件(接口)放一个助手。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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
}


接口的概念是表示一个契约,而不是实现。

我现在还记不清IL是否真的允许静态方法在接口中实现——我有一种潜移默化的怀疑,它确实允许静态方法实现——但这在一定程度上混淆了概念。

我可以理解你的观点——有时知道哪些辅助方法是可用的,它们与接口相连(扩展方法在这里特别相关),这很有用,但是我个人还是想把它们放在一个单独的类中,只是为了保持心智模型的整洁。


我遇到过几次,做了一些研究。可悲的是,我确实支持这一点。我对这件事很失望,写了一篇博客。你可以在这里找到它。


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

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

dotnetjunkie网站由totaldevpro提供…所以谷歌缓存版本是唯一可用的版本

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

[…]

使用ILASM编译以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
.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
 }
}

然后可以调用此代码

1
2
3
4
5
6
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);
}

IntelliSense(VS)在此处无法按预期工作。它将静态方法识别为接口的实例方法,并且代码(如果遵循intellisense提示)看起来像要编译一样。C编译器(MS C)不编译代码,因为C不支持接口上实现的静态方法,并且只能通过反射从C调用。

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


出于您的目的,最好将插件接口与插件加载程序实现分离:这将使您的设计更不耦合,更具凝聚力(从而降低复杂性)。

关于"接口中的静态方法",请参见。

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


一个接口就是一个接口。它不是用来描述行为的。当一个类实现一个接口时,该类只会说"我保证用这些签名提供方法/事件等"。

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


接口的目的是声明一个对象的接口,通过这个接口可以访问它。由于这是它的唯一目的,所以允许代码放在接口中是没有意义的。如果仍然想向接口添加一些代码,可以使用扩展方法。


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

您要做的是:

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

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