Подтвердить что ты не робот

Почему интерфейсы не могут указывать статические методы?

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

  • Почему интерфейсы не имеют сигнатур статических методов? Я попытаюсь вытеснить не-ответы, спрашивая, почему в мире я хотел бы сделать это со следующим: я хотел бы иметь возможность статически вызывать 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";
        }
    }
    
  • С другой стороны, и это вопрос этого второго вопроса, если вы знаете хорошую альтернативу для достижения вышеупомянутой цели, то непременно...

4b9b3361

Ответ 1

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

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

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

Это вызовет статический член на конкретном типе T.

Ответ 2

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

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

Ответ 3

Ответ Jon охватывает почти все, поэтому мой ответ включает в себя только возможную работу с использованием 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);
    }
}

Ответ 4

Может быть, несколько полезно, если интерфейс может указать статический класс, так что члены этого класса будут рассматриваться компилятором как статические члены этого интерфейса (таким образом, вместо того, чтобы использовать статический класс Enumerable<T> для получения Enumerable<T>.Default, вместо этого можно синтаксически указать IEnumerable<T>.Default. Было бы еще более полезно, если бы интерфейс мог указать, что некоторые такие статические методы должны использоваться по методу, подобному методам расширения, но без каких-либо странных правил определения области, связанных с ними ( так что интерфейс может появиться, чтобы предложить несколько "удобных" перегрузок для некоторых функций-членов, не требуя при этом всех реализаций для их предоставления.) Было бы чрезвычайно полезно, если бы в сочетании с такой функцией методы интерфейса могли быть объявлены "необязательными", так что когда реализация предоставила метод, который будет использоваться, и когда он не будет автоматически заменен метод расширения-ish, это, вероятно, потребует изменений в CLR.

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

Ответ 5

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

но они как бы вы его поместили, вы просто сделаете функцию static в абстрактном классе

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

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";
  }
}

Ответ 6

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

Вместо

// 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();
}

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

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