Почему C # (или .NET) не позволяет нам помещать статический / разделяемый метод в интерфейс? - PullRequest
7 голосов
/ 30 июня 2009

Почему 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<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
}

Ответы [ 7 ]

11 голосов
/ 30 июня 2009

Идея интерфейса - представлять договор, а не реализацию.

Я не могу вспомнить, действительно ли IL допускает статические методы с реализациями в интерфейсах - у меня есть хитрое подозрение, что это так - но это несколько запутывает концепцию.

Я понимаю вашу точку зрения - иногда полезно знать, какие вспомогательные методы доступны, которые связаны с интерфейсом (и методы расширения там особенно актуальны), но я бы лично хотел поместить их в отдельный класс в любом случае, просто чтобы держите умственную модель в чистоте.

6 голосов
/ 30 июня 2009

Я сталкивался с этим несколько раз и провел некоторое исследование. Грустная часть, IL фактически поддерживает это. Я так расстроился, что написал в блоге об этом. Вы можете найти его здесь .

2 голосов
/ 30 июня 2009

Проверьте мою запись в блоге о статических методах, реализованных в интерфейсах (извините за бесстыдную ссылку на себя)

[удалено неработающая ссылка http: / ...]

Сайт dotnetjunkies ткнул Totaldevpro ... так что кэшированная версия Google является единственной доступной

Edit: Я вставил кешированную версию ниже, я нашел:

[...]

Используйте 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);
}

Intellisense (VS) не работает здесь должным образом. Он распознал статический метод как метод экземпляра интерфейса, и код (если он следует подсказкам intellisense) выглядит все по порядку, как если бы он собирался компилироваться. Компилятор C # (MS C #) не компилирует код, поскольку C # не поддерживает реализованные статические методы на интерфейсах и может вызываться из C # только через отражение.

Я не тестировал другие IDE, такие как SharpDevelop ... поэтому пока не знаю, как бы он справился с этой ситуацией.

2 голосов
/ 30 июня 2009

Для вашей цели будет гораздо лучше отделить интерфейс плагина от реализации загрузчика плагинов: это сделает ваш дизайн намного менее связанным и более связным (таким образом уменьшая сложность).

Что касается "статических методов в интерфейсе", см. this .

И как замечание: вы действительно не хотите изобретать еще одну архитектуру плагинов: взгляните на MEF .

1 голос
/ 30 июня 2009

Интерфейс - это просто интерфейс. Он не предназначен для описания поведения. Когда класс реализует интерфейс, он просто говорит: «Я обещаю, что предоставлю методы / события / и т. Д. С этими сигнатурами».

Вам нужен интерфейс без статического метода и абстрактный базовый класс, который реализует интерфейс и статический метод. Тогда другие классы могут наследовать от базового класса и изменять реализации метода интерфейса. Но даже это сомнительный дизайн.

0 голосов
/ 30 июня 2009

Цель интерфейса - объявить интерфейс объекта, через который он может быть доступен. Из-за того, что это является его единственной целью, не имеет смысла разрешать размещение кода в интерфейсе. Если вы все еще хотите добавить некоторый код в интерфейс, вы можете использовать методы расширения.

0 голосов
/ 30 июня 2009

статические методы связаны с типом, в котором они объявлены, и не имеют отношения к переопределению. Если бы вы могли прикрепить статический метод к интерфейсу, вам бы пришлось ссылаться на него через сам интерфейс, например, ITaskPlugin.GetPlugins(...)

То, что вы хотите сделать, это либо:

1) Поместите ваш метод в абстрактный базовый класс, так как интерфейсы не предназначены для хранения кода реализации, или

2) Создайте метод расширения, который применяется к интерфейсу, и тогда у вас будет доступ к нему без использования базового класса.

...