2012-05-31 5 views
41

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

  1. Почему не интерфейсы имеют статические сигнатуры метода? Постараюсь упредить не-ответы спрашивают, почему в мире, я хотел бы сделать это следующим: Я хотел бы быть в состоянии статически вызвать GetDbConnectionType() на SqliteCodeGenerator и MssqlCodeGenerator:

    interface ICodeGenerator 
    { 
        // this is the method I would like to be static: 
        string GetDbConnectionType(); 
    } 
    
    abstract class CodeGeneratorBase : ICodeGenerator 
    { 
        public abstract string GetDbConnectionType(); 
    
        public void GenerateSomeCode(StringBuilder s) 
        { 
         s.AppendLine("var foo = new " + GetDbConnectionType() + "();"); 
        } 
    } 
    
    class SqliteCodeGenerator : CodeGeneratorBase 
    { 
        public override string GetDbConnectionType() 
        { 
         return "SQLiteConnection"; 
        } 
    } 
    
    class MssqlCodeGenerator : CodeGeneratorBase 
    { 
        public override string GetDbConnectionType() 
        { 
         return "SqlConnection"; 
        } 
    } 
    
  2. На с другой стороны, и это вопрос второй вопрос, если вы знаете о хорошей альтернативой для достижения вышеупомянутой цели, а затем всеми средствами ...

+0

Неправильный DUP, это это одна: http://stackoverflow.com/questions/248263/why-cant- i-declare-c-sharp-methods-virtual-and-static – Joe

+0

Возможный дубликат [Почему C# (или .NET) не позволяет нам статический/совместно используемый метод внутри интерфейса?] (http: // stackoverflow .com/questions/1062468/why-shouldnt-cor-net-allow-us-to-put-a-static-shared-method-in-an-inter) – nawfal

+0

Возможный дубликат [Почему я не могу объявить методы C# виртуальный и статический?] (https://stackoverflow.com/questions/248263/why- cant-i-declare-c-sharp-methods-virtual-and-static) – JHG

ответ

64

Предположим, вы могли указать в интерфейсе, тип должен был иметь определенный статический метод ... как wou Вы называете это? Полиморфизм работает через экземпляров - тогда как статические члены явно не используют экземпляры.

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

// This isn't valid code... 
public void Foo<T>() where T : ICodeGenerator 
{ 
    string type = T.GetDbConnectionType(); 
} 

Это было бы вызвать статический элемент по типу Tбетон.

У меня есть blogged more about this, но я подозреваю, что это не оправдывает сложность.

С точки зрения альтернатив - обычно у вас должен быть другой интерфейс и есть отдельные типы для реализации этого интерфейса. Это хорошо работает в некоторых контекстах, но не в других.

+1

Члены родового типа 'T: BaseType' всегда привязаны к' BaseType', а не 'T'. Если эти члены в «BaseType» являются виртуальными, привязка к слотам vtable, определенным в «BaseType». Виртуальные участники, вызываемые по ссылке типа 'T', отправляются через номера слотов, определенные типом' BaseType'; тип 'T' не играет никакой роли в отправке. Возможно, было бы полезно разрешить типам объявлять и переопределять виртуальные статические методы таким образом, чтобы, с немного большей магией, 'T.VirtualStaticMethod()' будет отправляться через vtable таблицы 'T', используя слот, определенный в' BaseType', но это было бы большим изменением. – supercat

+3

Кроме того, существует преимущество форсирования типа для реализации статических элементов, а также запросов для типов, реализующих интерфейс, и возможности ожидать существования статического метода. –

+0

@JonSkeet, ваш пост в блоге нарушен. У вас есть обновление? Это именно то, что мне нужно. Как это изменилось за последние три года, если прежний пост в блоге уже не уместен. Спасибо! – RLH

1

Ответ Джона охватывает почти все, поэтому мой ответ включает в себя только возможную работу с использованием API конфигурации .NET. Это требует немного синтаксических издержек, но это дает вам статический доступ к экземпляру.

interface IStorage 
{ 
    void Store(string item); 
} 

static class Storage 
{ 
    private static readonly IStorage _instance; 

    static Storage() 
    { 
     var storageTypeString = ConfigurationManager.AppSettings["storageTypeString"]; 
     var storageType = Type.GetType(storageTypeString, true); 
     _instance = (IStorage)Activator.CreateInstance(storageType); 
    } 

    public static void Store(string item) 
    { 
     _instance.Store(item); 
    } 
} 
1

Это может быть несколько полезно, если интерфейс может указать статический класс, таким образом, чтобы члены этого класса будет рассматриваться компилятором как статические члены этого интерфейса. Таким образом, вместо того, чтобы использовать статический класс Enumerable<T> для получения Enumerable<T>.Default, можно вместо этого синтаксически указать IEnumerable<T>.Default.

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

Было бы чрезвычайно полезно, если бы в сочетании с такой особенностью методы интерфейса могли быть объявлены «необязательными», так что когда реализация предоставила метод, который будет использоваться, а когда это не было, метод расширения-ish автоматически заменяется. Однако это, вероятно, потребует изменений в CLR.

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

7

@JonSkeet: возможно create a static interface member in CIL, поэтому я боюсь, что ваше первое утверждение вводит в заблуждение. Я предполагаю, что он был исключен из C# в качестве выбора дизайна командой Microsoft для поощрения правильного использования интерфейсов.

Лучший способ получить эту функциональность, вероятно, с помощью extension methods, это позволит вам добавить метод ко всем наследникам вашего интерфейса или к конкретной реализации этого интерфейса, однако вам нужно написать отдельный класс для проведения реализации метода расширения, который (если не планируется) можно легко потерять.

0

Я знаю, что это старый, но на самом деле вы можете со статическими функциями, объявленными в статическом классе за пределами пространства имен.

, но они, как ваш положить его, вы бы просто сделать функцию статической в ​​абстрактном классе

сделать это из интерфейса вы делаете это

public static class Interfacefunction{ 
    public static string GetDbConnectionType(this ICodeGenerator me) 
    { 
     // this is the method I would like to be static: 
     // you can even get access to me 
     return "SQLiteConnection"; 
    } 
} 
0

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

Вместо:

// does not compile 
ISomeInterface { 
    static void DoSomething(); 
    static bool TestSomething(string pValue); 
    // etc... 
} 

static class SomeStaticClass : ISomeInterface { 
    public static void DoSomething() { 
    } 

    public static bool TestSomething(string pValue) { 
    } 
} 

Определение класса (сделать его общим, если логика должна варьироваться между классами, которые вы используете его):

sealed class SomeClass { 
    public void DoSomething() { 
     // reusable implementation 
    } 

    public bool TestSomething(string pValue) { 
     // reusable implementation 
    } 
} 

и дать статический экземпляр этого класса к вашему статическому классу:

static class SomeStaticClass { 
    static readonly SomeClass sSomeClass = new SomeClass(); 
} 

Единственная проблема заключается в том, что вам необходимо решить, следует ли e Xposé свойство статического экземпляра:

static class SomeStaticClass { 
    static readonly SomeClass sSomeClass = new SomeClass(); 

    public static SomeClass SomeProperty { get { return sSomeClass; } } 
} 

... 

SomeStaticClass.SomeProperty.DoSomething(); 
if (SomeStaticClass.SomeProperty.TestSomething(someValue)) 
    ... 

или обернуть его методы:

static class SomeStaticClass { 
    static readonly SomeClass sSomeClass = new SomeClass(); 

    public static void DoSomething() { 
     sSomeClass.DoSomething(); 
    } 

    public static bool TestSomething(string pValue) { 
     sSomeClass.TestSomething(pValue); 
    } 
} 

... 

SomeStaticClass.DoSomething(); 
if (SomeStaticClass.TestSomething(someValue)) 
    ... 
Смежные вопросы