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

person Hao    schedule 30.06.2009    source источник
comment
Думаю, вы сможете сделать это на C # 8!   -  person AroglDarthu    schedule 19.06.2019
comment
См .: docs.microsoft.com/en- us / dotnet / csharp / tutorials /   -  person AroglDarthu    schedule 19.06.2019


Ответы (7)


Идея интерфейса состоит в том, чтобы представлять контракт, а не реализацию.

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

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

person Jon Skeet    schedule 30.06.2009
comment
+1 за чистоту ментальной модели. но в моем примере я думаю, что реализация помощника непосредственно внутри интерфейса немного более интуитивно понятна. и методы расширения здесь даже не помогут, вспомогательному методу не нужен объект this из экземпляра ITaskPlugin, чтобы он мог получить все доступные плагины - person Hao; 30.06.2009
comment
Это было бы полезно при работе с дженериками. Примеры: dim x = T.GetDefaultValue (), dim x = T.New (withActualArguments) и т. Д. Многие интерфейсы кажутся статичными (в основном IComparable и другие). - person Craig Gidney; 30.06.2009
comment
В настоящее время в IL нет концепции статического интерфейса, но я предложил именно это - именно для параметров типа: msmvps.com/blogs/jon_skeet/archive/2008/08/29/ - person Jon Skeet; 30.06.2009
comment
IL явно поддерживает статические конструкторы в интерфейсах - спецификация IL 10.5.3 - person thecoop; 20.01.2011
comment
@thecoop: статические конструкторы нельзя вызывать так же, как статические методы, хотя ... на самом деле это не одно и то же. - person Jon Skeet; 20.01.2011
comment
@Jon: ах, неправильно прочитал ответ. IL также позволяет использовать статические методы и поля на интерфейсах. - person thecoop; 20.01.2011
comment
Так почему мы не можем дать им определение? Я бы хотел иметь возможность поставить static (this type) Deserialize(XmlDocument source); на несколько своих интерфейсов. - person Flynn1179; 24.02.2011

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

person Jonathan van de Veen    schedule 30.06.2009
comment
рад, что нашел кого-то, кто разделяет то же мнение. кстати, я не могу прочитать ваш сайт. blogspot запрещен в Китае. Мой обходной путь раньше - попытаться загрузить интересующий веб-сайт на translate.google.com. но теперь даже перевод веб-страницы запрещен - person Hao; 30.06.2009
comment
Слишком плохой блог забанен. Там много очень интересных статей. Можете ли вы получить корм для кормораздатчика? В таком случае вы можете найти канал моего блога здесь: feeds.feedburner.com/Developers42 - person Jonathan van de Veen; 01.07.2009
comment
+1 за ссылку на мой блог в сообщении, на которое вы ссылаетесь ;-), блог мертв, но я разместил код выше. - person MaLio; 08.12.2009
comment
Вы можете обновить свой блог новыми функциями C # 8. Он позволяет использовать статические элементы в интерфейсах: docs.microsoft.com/en-us/dotnet/csharp/tutorials/ - person AroglDarthu; 19.06.2019

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

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

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

person Anton Gogolev    schedule 30.06.2009
comment
Я уже включил эту ссылку в свой пост выше :-) Я знаю, что вы имеете в виду с меньшим дизайном связи, но для моего примера выше я думаю, что размещение вспомогательного метода внутри интерфейса оправдано, imho - person Hao; 30.06.2009
comment
Нет, это абсолютно неоправданно. Загрузка плагинов абсолютно не входит в обязанности плагина. - person Anton Gogolev; 30.06.2009
comment
Я не понимаю, почему это должно быть абсолютным. в любом случае, я просто больше думаю о принципе С # универсального магазина - person Hao; 30.06.2009
comment
спасибо за MEF, но он все еще находится в стадии разработки. пока я просто откатываю свой собственный фреймворк для плагинов, все равно это не ракетостроение - person Hao; 30.06.2009
comment
MEF - это то, во что встроен новый редактор Visual Studio. Он должен быть довольно стабильным. - person John Saunders; 30.06.2009

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

[удалена битая ссылка 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);
}

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

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

person MaLio    schedule 30.06.2009
comment
-1 ссылка мертва, вместо имени домена указан ip, в кэше сейчас ничего не отображается - person Maslow; 29.07.2009
comment
Исправлено ... вставлено в запись - person MaLio; 28.08.2009

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

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

person colithium    schedule 30.06.2009

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

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

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

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

person Nathan Ridley    schedule 30.06.2009

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

person Oliver Hanappi    schedule 30.06.2009