Вопрос:

Почему C # не позволяет статическим методам реализовать интерфейс?

c# oop language-features

180232 просмотра

26 ответа

5284 Репутация автора

Почему C # был разработан таким образом?

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

Если классы хотят реализовать это поведение в общем методе, почему бы и нет?

Вот пример того, что я имею в виду:

// These items will be displayed in a list on the screen.
public interface IListItem {
  string ScreenName();
  ...
}

public class Animal: IListItem {
    // All animals will be called "Animal".
    public static string ScreenName() {
        return "Animal";
    }
....
}

public class Person: IListItem {

    private string name;

    // All persons will be called by their individual names.
    public string ScreenName() {
        return name;
    }

    ....

 }
Автор: Kramii Reinstate Monica Источник Размещён: 03.11.2008 03:42

Ответы (26)


3 плюса

323506 Репутация автора

Потому что интерфейсы находятся в структуре наследования, а статические методы не наследуются хорошо.

Автор: Joel Coehoorn Размещён: 03.11.2008 03:43

14 плюса

88700 Репутация автора

Я думаю, близорукость.

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

IMyInterface val = GetObjectImplementingIMyInterface();
val.SomeThingDefinedinInterface();

Только с введением интерфейсов, поскольку ограничения для обобщений сделали практическое использование добавления статического метода к интерфейсу.

(отвечает на комментарий :) Я полагаю, что изменение этого сейчас потребовало бы изменения в CLR, что привело бы к несовместимости с существующими сборками.

Автор: James Curran Размещён: 03.11.2008 03:46

14 плюса

5921 Репутация автора

Интерфейсы определяют поведение объекта.

Статические методы определяют не поведение объекта, а поведение, которое каким-то образом влияет на объект.

Автор: John Kraft Размещён: 03.11.2008 03:47

214 плюса

27752 Репутация автора

Решение

Предположим, вы спрашиваете, почему вы не можете сделать это:

public interface IFoo {
    void Bar();
}

public class Foo: IFoo {
    public static void Bar() {}
}

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


Чтобы реализовать ваш пример, я бы дал Animal свойство const, которое все равно позволяло бы к нему доступ из статического контекста, и возвращал бы это значение в реализации.

public class Animal: IListItem {
    /* Can be tough to come up with a different, yet meaningful name!
     * A different casing convention, like Java has, would help here.
     */
    public const string AnimalScreenName = "Animal";
    public string ScreenName(){ return AnimalScreenName; }
}

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

Автор: Chris Marasti-Georg Размещён: 03.11.2008 03:57

3 плюса

168102 Репутация автора

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

Были бы бесконечные дебаты о том, имеет ли это значение, что является наилучшей практикой, и есть ли проблемы с производительностью, делающие это так или иначе. Просто не поддерживая его, C # избавляет нас от необходимости беспокоиться об этом.

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

Автор: AnthonyWJones Размещён: 03.11.2008 04:33

1 плюс

40928 Репутация автора

Интерфейсы - это абстрактные наборы определенных доступных функций.

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

Если бы методы были определены как статические, класс, реализующий интерфейс, не был бы настолько инкапсулирован, как мог бы. Инкапсуляция - это хорошая вещь, к которой нужно стремиться в объектно-ориентированном дизайне (я не буду вдаваться в причину, вы можете прочитать это здесь: http://en.wikipedia.org/wiki/Object-oriented ). По этой причине статические методы не допускаются в интерфейсах.

Автор: Scott Langham Размещён: 03.11.2008 04:34

1 плюс

11236 Репутация автора

К вашему сведению: вы можете получить поведение, похожее на то, что вы хотите, создав методы расширения для интерфейса. Метод расширения будет общим, не переопределяемым статическим поведением. Однако, к сожалению, этот статический метод не будет частью контракта.

Автор: Daniel Auger Размещён: 03.11.2008 04:46

3 плюса

40928 Репутация автора

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

Автор: Scott Langham Размещён: 03.11.2008 04:49

0 плюса

6351 Репутация автора

Я думаю, что короткий ответ - «потому что он бесполезен». Чтобы вызвать метод интерфейса, вам нужен экземпляр типа. Из методов экземпляра вы можете вызывать любые статические методы, которые захотите.

Автор: mackenir Размещён: 03.11.2008 05:29

166 плюса

77999 Репутация автора

Моя (упрощенная) техническая причина заключается в том, что статические методы отсутствуют в vtable , а сайт вызовов выбирается во время компиляции. Это та же самая причина, по которой вы не можете иметь переопределенные или виртуальные статические члены. Для более подробной информации, вам понадобится выпускник CS или компилятор - а я ни один из них.

По политическим причинам я процитирую Эрика Липперта (который является победителем компилятора и имеет степень бакалавра математики, информатики и прикладной математики из Университета Ватерлоо (источник: LinkedIn ):

... основной принцип разработки статических методов, принцип, который дает им их имя ... [есть] ... всегда можно точно определить, во время компиляции, какой метод будет вызван. То есть метод может быть решен исключительно путем статического анализа кода.

Обратите внимание, что Липперт оставляет место для так называемого метода типа:

То есть метод, связанный с типом (например, статическим), который не принимает необнуляемый аргумент «this» (в отличие от экземпляра или виртуального), но метод, в котором вызываемый метод будет зависеть от созданного типа T ( в отличие от статического, который должен быть определен во время компиляции).

но еще предстоит убедиться в его полезности.

Автор: Mark Brackett Размещён: 03.11.2008 05:37

9 плюса

119747 Репутация автора

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

Как бы вы назвали это ??


public interface MyInterface { void MyMethod(); }
public class MyClass: MyInterface
{
    public static void MyMethod() { //Do Something; }
}

 // inside of some other class ...  
 // How would you call the method on the interface ???
    MyClass.MyMethod();  // this calls the method normally 
                         // not through the interface...

    // This next fails you can't cast a classname to a different type... 
    // Only instances can be Cast to a different type...
    MyInterface myItf = MyClass as MyInterface;  
Автор: Charles Bretana Размещён: 03.11.2008 05:51

3 плюса

0 Репутация автора

Чтобы привести пример, где мне не хватает либо статической реализации методов интерфейса, либо того, что Марк Брэкетт представил как «метод так называемого типа»:

При чтении из хранилища базы данных у нас есть универсальный класс DataTable, который обрабатывает чтение из таблицы любой структуры. Вся специфичная для таблицы информация помещается в один класс для каждой таблицы, которая также содержит данные для одной строки из БД и должна реализовывать интерфейс IDataRow. В IDataRow включено описание структуры таблицы для чтения из базы данных. DataTable должен запросить структуру данных из IDataRow перед чтением из БД. В настоящее время это выглядит так:

interface IDataRow {
  string GetDataSTructre();  // How to read data from the DB
  void Read(IDBDataRow);     // How to populate this datarow from DB data
}

public class DataTable<T> : List<T> where T : IDataRow {

  public string GetDataStructure()
    // Desired: Static or Type method:
    // return (T.GetDataStructure());
    // Required: Instantiate a new class:
    return (new T().GetDataStructure());
  }

}

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

Автор: Jesper Grooss Размещён: 26.02.2009 07:16

1 плюс

11 Репутация автора

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

У меня было несколько классов Static Business Layer, в которых реализованы такие методы CRUD, как «Создать», «Чтение», «Обновление», «Удалить» для каждого типа сущностей, таких как «Пользователь», «Команда» и т. Д. Затем я создал базу элемент управления с абстрактным свойством для класса бизнес-уровня, в котором реализованы методы CRUD. Это позволило мне автоматизировать операции «Создать», «Чтение», «Обновить», «Удалить» из базового класса. Мне пришлось использовать Синглтон из-за статического ограничения.

Автор: Louis Rebolloso Размещён: 19.01.2011 05:18

90 плюса

909 Репутация автора

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

Предположим, у нас есть параметр типа в универсальном методе, и нам нужно выполнить с ним некоторую операцию. Мы не хотим мгновенно, потому что мы не знаем конструкторов.

Например:

Repository GetRepository<T>()
{
  //need to call T.IsQueryable, but can't!!!
  //need to call T.RowCount
  //need to call T.DoSomeStaticMath(int param)
}

...
var r = GetRepository<Customer>()

К сожалению, я могу придумать только «некрасивые» альтернативы:

  • Используйте рефлексию Гадкий и бьет идею интерфейсов и полиморфизма.

  • Создать полностью отдельный заводской класс

    Это может значительно увеличить сложность кода. Например, если мы пытаемся смоделировать доменные объекты, каждому объекту потребуется другой класс репозитория.

  • Создайте экземпляр и затем вызовите нужный метод интерфейса

    Это может быть трудно реализовать, даже если мы контролируем источник для классов, используемых в качестве общих параметров. Причина в том, что, например, нам может потребоваться, чтобы экземпляры были только в хорошо известном состоянии «подключен к БД».

Пример:

public class Customer 
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  void SomeOtherMethod() 
  { 
    //do work...
  }
}

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

public class Customer: IDoSomeStaticMath
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  //dummy instance
  public Customer() { IsDummy = true; }

  int DoSomeStaticMath(int a) { }

  void SomeOtherMethod() 
  { 
    if(!IsDummy) 
    {
      //do work...
    }
  }
}

Это явно уродливо, а также ненужно усложняет код для всех других методов. Очевидно, тоже не элегантное решение!

Автор: Ivan Arjentinski Размещён: 30.10.2011 01:35

18 плюса

60942 Репутация автора

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

строка DoSomething  () где T: ISomeFunction
{
  если (T.someFunction ())
    ...
}

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

  1. Создайте статический универсальный класс, чей параметр типа будет тем типом, который вы передадите в DoSomething выше. Каждая вариация этого класса будет иметь один или несколько статических членов, содержащих вещи, связанные с этим типом. Эта информация может быть предоставлена ​​либо путем вызова каждого интересующего класса подпрограммы «регистрация информации», либо с помощью Reflection для получения информации при запуске статического конструктора варианта класса. Я считаю, что последний подход используется такими вещами, как Comparer .Default ().
  2. Для каждого интересующего класса T определите класс или структуру, которая реализует IGetWhwhatClassInfo и удовлетворяет «новому» ограничению. Класс на самом деле не будет содержать никаких полей, но будет иметь статическое свойство, которое возвращает статическое поле с информацией о типе. Передайте тип этого класса или структуры в соответствующую стандартную процедуру, которая сможет создать экземпляр и использовать его для получения информации о другом классе. Если вы используете класс для этой цели, вам, вероятно, следует определить статический универсальный класс, как указано выше, чтобы избежать необходимости каждый раз создавать новый экземпляр объекта дескриптора. Если вы используете структуру, стоимость создания экземпляра должна быть равна нулю, но каждый другой тип структуры потребует другого расширения подпрограммы DoSomething.

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

Автор: supercat Размещён: 17.11.2011 05:05

14 плюса

798 Репутация автора

В той степени, в которой интерфейсы представляют «контракты», для статических классов представляется разумным реализовать интерфейсы.

Все вышеперечисленные аргументы, похоже, упускают этот момент в отношении контрактов.

Автор: George Размещён: 22.08.2012 03:47

1 плюс

337 Репутация автора

Большинство людей, кажется, забывают, что в ООП классы тоже являются объектами, и поэтому у них есть сообщения, которые по какой-то причине c # называет «статическим методом». Тот факт, что существуют различия между объектами экземпляра и объектами класса, показывает только недостатки или недостатки в языке. Оптимист о c # хотя ...

Автор: Mar Bar Размещён: 15.07.2013 06:55

1 плюс

31 Репутация автора

Хорошо, вот пример необходимости «метода типа». Я создаю один из набора классов на основе исходного XML. Итак, у меня есть

  static public bool IsHandled(XElement xml)

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

Функция должна быть статической, так как в противном случае мы тратим время на создание неподходящих объектов. Как отмечает @Ian Boyde, это можно сделать на фабричном классе, но это только добавляет сложности.

Было бы неплохо добавить его в интерфейс, чтобы заставить разработчиков классов реализовать его. Это не вызовет значительных накладных расходов - это только проверка времени компиляции / компоновки и не влияет на vtable.

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

Так что это небольшая потенциальная особенность, которую лучше всего не учитывать.

Автор: Stephen Westlake Размещён: 29.07.2013 01:20

4 плюса

325 Репутация автора

Что касается статических методов, используемых в неуниверсальных контекстах, я согласен с тем, что не имеет смысла разрешать их в интерфейсах, поскольку вы не сможете вызывать их, если у вас все равно есть ссылка на интерфейс. Однако есть фундаментальная дыра в дизайне языка, созданная с использованием интерфейсов НЕ в полиморфном контексте, а в общем. В этом случае интерфейс вообще не интерфейс, а скорее ограничение. Поскольку в C # отсутствует концепция ограничения вне интерфейса, в нем отсутствует существенная функциональность. Дело в точке:

T SumElements<T>(T initVal, T[] values)
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

Здесь нет полиморфизма, универсальный использует фактический тип объекта и вызывает оператор + =, но это не удается, поскольку он не может точно сказать, что этот оператор существует. Простое решение состоит в том, чтобы указать его в ограничении; простое решение невозможно, потому что операторы являются статическими, а статические методы не могут быть в интерфейсе, и (здесь проблема) ограничения представлены в виде интерфейсов.

То, что нужно C #, - это реальный тип ограничений, все интерфейсы также будут ограничениями, но не все ограничения будут интерфейсами, тогда вы могли бы сделать это:

constraint CHasPlusEquals
{
    static CHasPlusEquals operator + (CHasPlusEquals a, CHasPlusEquals b);
}

T SumElements<T>(T initVal, T[] values) where T : CHasPlusEquals
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

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

Автор: Jeremy Sorensen Размещён: 13.08.2013 05:26

0 плюса

9233 Репутация автора

Я думаю, что вопрос заключается в том, что C # нуждается в другом ключевом слове, именно для такой ситуации. Вам нужен метод, возвращаемое значение которого зависит только от типа, для которого он вызывается. Вы не можете назвать это «статическим», если указанный тип неизвестен. Но как только тип станет известным, он станет статичным. «Неразрешенная статика» - это идея - она ​​еще не статична, но как только мы узнаем тип приема, он будет. Это очень хорошая концепция, поэтому программисты постоянно просят об этом. Но это не совсем соответствовало тому, как дизайнеры думали о языке.

Поскольку он недоступен, я привык использовать нестатические методы, как показано ниже. Не совсем идеально, но я не вижу подходов, которые имеют больше смысла, по крайней мере, не для меня.

public interface IZeroWrapper<TNumber> {
  TNumber Zero {get;}
}

public class DoubleWrapper: IZeroWrapper<double> {
  public double Zero { get { return 0; } }
}
Автор: William Jockusch Размещён: 19.03.2015 04:25

1 плюс

36 Репутация автора

Тот факт, что статический класс реализован в C # корпорацией Microsoft, создавая специальный экземпляр класса со статическими элементами, является лишь странностью того, как достигается статическая функциональность. Это не теоретический момент.

Интерфейс ДОЛЖЕН быть дескриптором интерфейса класса - или того, как с ним взаимодействуют, и это должно включать статические взаимодействия. Общее определение интерфейса (от Meriam-Webster): место или область, в которой разные вещи встречаются, общаются или влияют друг на друга. Когда вы полностью опускаете статические компоненты класса или статические классы, мы игнорируем большие разделы о том, как эти плохие парни взаимодействуют.

Вот очень четкий пример того, где возможность использовать интерфейсы со статическими классами была бы весьма полезна:

public interface ICrudModel<T, Tk>
{
    Boolean Create(T obj);
    T Retrieve(Tk key);
    Boolean Update(T obj);
    Boolean Delete(T obj);
}

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

Автор: Thomas Phaneuf Размещён: 26.07.2015 12:22

1 плюс

851 Репутация автора

C # и CLR должны поддерживать статические методы в интерфейсах, как это делает Java. Модификатор static является частью определения контракта и имеет значение, в частности, то, что поведение и возвращаемое значение не меняются в зависимости от экземпляра, хотя оно все еще может варьироваться от вызова к вызову.

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

Автор: Daniel Barbalace Размещён: 18.01.2016 10:33

0 плюса

1 Репутация автора

Согласно объектно-ориентированной концепции интерфейс реализуется классами и имеет контракт для доступа к этим реализованным функциям (или методам) с использованием объекта.

Поэтому, если вы хотите получить доступ к методам Interface Contract, вам нужно создать объект. Это всегда должно быть запрещено в случае статических методов. Статические классы, метод и переменные никогда не требуют объектов и загружаются в память без создания объекта этой области (или класса), или вы можете сказать, что не требуют создания объектов.

Автор: Sanjeev Saraswat Размещён: 02.07.2017 01:31

-1 плюса

67 Репутация автора

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

Автор: Vinay Chanumolu Размещён: 05.06.2018 12:47

0 плюса

339 Репутация автора

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

Для текущей реализации языка C # ограничение связано с разрешением наследования базового класса и интерфейсов. Если «класс SomeBaseClass» реализует «интерфейс ISomeInterface» и «класс SomeDerivedClass: SomeBaseClass, ISomeInterface» также реализует интерфейс, статический метод для реализации метода интерфейса может не скомпилироваться, поскольку статический метод не может иметь такую ​​же сигнатуру, как метод экземпляра (что присутствовать в базовом классе для реализации интерфейса).

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

Таким образом, это просто сводится к ограничению конфликта имен C #, например, и статических методов с одним и тем же именем в наследовании. Нет причин, по которым C # не может быть «обновлен» для поддержки контрактов статических методов (интерфейсов).

Автор: Greg McPherran Размещён: 14.05.2019 07:07

-1 плюса

11 Репутация автора

не делайте этого, попробуйте вместо этого абстрактные классы

public abstract class ExampleBase
{
    /// <summary>
    /// Do it
    /// </summary>
    public virtual abstract static void DoIt();
}
Автор: Eric Silveira Размещён: 29.06.2019 06:43
32x32