views:

280

answers:

7

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

seemingly duplicate from here. but my idea is a bit different one, I just want to put a helper for my plugins(interface)

shouldn't C# at least allow this idea?

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
}
+2  A: 

For your purpose, it will be much better to decouple plugin interface from plugin loader implementation: this will make your design much less coupled and more cohesive (thus reducing complexity).

As for "static methods in interface", see this.

And as a sidenote: you don't really want to invent yet another plugin architecture: take a look at MEF.

Anton Gogolev
i already included that link on my post above :-) i know what you mean with less coupling design, but for my example above, i think placing the helper method inside an interface is warranted, imho
Hao
No, it's absolutely not warranted. Loading plugins is absolutely not the responsibility of a plugin.
Anton Gogolev
i can't see the point why it must be absolute. anyway, i'm just thinking more in C# principle of "one-stop shop"
Hao
thanks for the MEF, but it is still an on-going development. for now, i'll just roll my own framework for plugins, it's not that rocket science anyhow
Hao
MEF is what the new Visual Studio Editor is built in. It has to be pretty stable.
John Saunders
+5  A: 

The idea of an interface is to represent a contract, not implementation.

I can't remember offhand whether IL actually does allow static methods with implementations in interfaces - I've a sneaky suspicion that it does - but that muddies the concept somewhat.

I can see your point - it's sometimes useful to know what helper methods are available which are connected with an interface (and extension methods are particularly relevant there) but I would personally want to put those in a separate class anyway, just to keep the mental model clean.

Jon Skeet
+1 for "keep the mental model clean." but in my example, I think implementing the helper directly inside an interface is a bit more intuitive. and extension methods won't even help here, the helper method don't need a this object from an ITaskPlugin instance in order for it to get all the available plugins
Hao
It would be useful when dealing with generics. Exampless: dim x = T.GetDefaultValue(), dim x = T.New(withActualArguments), etc. Many interfaces feel like they should be static (mainly IComparable and friends).
Strilanc
There's no concept of a "static interface" in IL at the moment, but I've proposed exactly this - precisely for type parameters: http://msmvps.com/blogs/jon_skeet/archive/2008/08/29/lessons-learned-from-protocol-buffers-part-4-static-interfaces.aspx
Jon Skeet
A: 

static methods are associated with the type in which they are declared and not relevant for overriding. If you were able to attach a static method to an interface, you would have to reference it via the interface itself, e.g. ITaskPlugin.GetPlugins(...)

What you want to do is either:

1) Put your method in an abstract base class, as interfaces are not designed to hold implementation code, or

2) Create an extension method which applies to the interface and then you'll have access to it without having to use a base class.

Nathan Ridley
A: 

An interface's purpose is to declare an object's interface through which it can be accessed. Due to the fact that this is its sole purpose, it would not make sense to allow code being placed in an interface. If you still want to add some code to an interface, you could use extension methods.

Oliver Hanappi
+1  A: 

An interface is just that, an interface. It isn't meant to be used to describe behavior. When a class implements an interface, the class just says "I promise that I provide methods/events/etc with these signatures".

What you want is an interface without the static method and an abstract base class that implements the interface and the static method. Then other classes can inherit from the base class and change the interface's method implementations. But even this is a questionable design.

colithium
+2  A: 

I've run into this several times and did some research. The sad part is, IL actually supports this. I got so frustrated with this I wrote a blog post about it. You can find it here.

Jonathan van de Veen
glad I found somebody who shares the same sentiment. btw, i cannot read your website. blogspot is banned here in China. my workaround before is to try to load a website of interest on translate.google.com. but now, even translating a webpage is banned
Hao
Too bad blogspot is banned. There are a lot of very interesting articles on there. Can you recieve a feedburner feed? If so you can find the feed to my blog here: http://feeds.feedburner.com/Developers42
Jonathan van de Veen
+1 for linking my blog in the post you link to ;-), the blog is dead, but I have posted the code above.
MaLio
A: 

Hi

Check out my blog entry on static methods implemented in interfaces (sorry for the shameless self reference)

[removed broken link http:/... ]

dotnetjunkies site is poked by totaldevpro ... so the google cached version is the only one available

Edit: I pasted a cached version below I found:

[...]

Use ILAsm to compile the following:

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

This code then can be called

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) does not work here as expected. It recognized the static method as an instance method of the interface, and the code (if following the intellisense prompts) looks all in order as if it were going to compile. The C# compiler (MS C#) does not compile the code as C# does not suppport implemented static methods on interfaces, and can from C# only be invoked via reflection.

I have not tested with other IDE's such as SharpDevelop ... so have no idea as yet how it would deal with this situation.

MaLio
-1 link is dead, ip referenced instead of a domain name, cached doesn't appear to be showing anything now
Maslow
Fixed ... pasted in the entry
MaLio