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


448

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

    ....

 }

6
Хорошо, у Java 8 есть это ( stackoverflow.com/questions/23148471/… ).
Лян

1
Посмотрите, как вы можете комбинировать статическое поведение с наследованием или реализацией интерфейса: stackoverflow.com/a/13567309/880990
Оливье Жако-Дескомбс

1
IListItem.ScreenName() => ScreenName()(используя синтаксис C # 7) реализует интерфейсный метод явно, вызывая статический метод. Тем не менее, все становится ужасно, когда вы добавляете наследование к этому (вы должны переопределить интерфейс)
Jeroen Mostert

2
Просто, чтобы все знали, что ожидание прошло! В C # 8.0 есть статические методы интерфейса: dotnetfiddle.net/Lrzy6y (хотя они работают немного иначе, чем то, как OP хотел, чтобы они работали - вам не нужно их реализовывать)
Jamie Twells,

Ответы:


221

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

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 и приму его в качестве указания на то, что он может не будет хорошей идеей


6
Прекрасный пример! Но я не уверен, что понимаю твои рассуждения. Конечно, компилятор мог бы быть спроектирован так, чтобы он также рассматривал члены statuc? Разве у экземпляров нет таблицы адресов для реализации этих методов? Разве статические методы не могут быть включены в эту таблицу?
Крами

12
Есть случай, когда это может быть полезно. Например, я хочу, чтобы все разработчики реализовали метод GetInstance, который принимает аргумент XElement. Я не могу ни определить это как статический метод в интерфейсе, ни потребовать сигнатуру конструктора из интерфейса.
oleks

25
Многие люди взвесили с обеих сторон: от «Это не имеет смысла» до «Это ошибка, я бы хотел, чтобы вы могли». (Я думаю, что есть допустимые варианты использования для этого, как я здесь и закончил.) В качестве обходного пути, метод экземпляра может просто делегировать статический метод.
Гарпо

5
Вы также можете просто реализовать кусок как метод расширения на базовом интерфейсе, как в: public static object MethodName(this IBaseClass base)внутри статического класса. Недостатком, однако, является то, что в отличие от наследования интерфейса - это не заставляет / позволяет отдельным наследникам хорошо переопределять методологию.
Трой Алфорд

8
Было бы много смысла с дженериками. Например, void Something <T> (), где T: ISomeInterface {new T (). DoSomething1 (); T.DoSomething2 (); }
Франциско Райан Толмаски,

173

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

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

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

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

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

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


5
Отлично, это ответ, который я хотел написать - я просто не знал деталей реализации.
Крис Марасти-Георг

5
Отличный ответ. И я хочу этот «метод типа»! Было бы полезно во многих случаях (подумайте о метаданных для типа / класса).
Филипп Добмайер

23
Это правильный ответ. Вы передаете интерфейс кому-то, он должен знать, как вызывать метод. Интерфейс - это просто таблица виртуальных методов . У вашего статического класса этого нет. Вызывающий не знает, как вызвать метод. (Перед тем, как я прочитал этот ответ я думал , C # просто был педантичным. Теперь я понимаю , что это техническое ограничение, налагаемое , что интерфейс является ). Другие люди будут говорить с вами о том, что это плохой дизайн. Это не плохой дизайн - это техническое ограничение.
Ян Бойд

3
Вполне возможно сгенерировать объект для статического класса со связанным vtable. Посмотрите, как Scala обрабатывает objects и как им разрешено реализовывать интерфейсы.
Себастьян Граф

3
Vtable может просто указывать на статическую реализацию, во многом как это Func<>делает, но вместо того, чтобы содержать только один указатель, он содержит все указатели для методов, требуемых интерфейсом. Использование staticlike в статической диспетчеризации делает искусственно затрудняющий язык ради «объектно-ориентированной».
Себастьян Граф

97

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

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

Например:

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

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


35
+1 для "Большинство ответов здесь, кажется, пропускают всю суть."; невероятно, как кажется, что почти все ответы уклоняются от сути вопроса, чтобы углубиться в в основном бесполезное словоблудие ...

5
@Chris Вот конкретный пример, который заставил меня снова поразить это ограничение. Я хочу добавить интерфейс IResettable к классам, чтобы указать, что они кэшируют определенные данные в статических переменных, которые могут быть сброшены администратором сайта (например, список категорий заказов, набор ставок НДС, список категорий, извлеченных из внешнего API) чтобы уменьшить количество обращений к БД и внешнему API, очевидно, будет использоваться сброс статического метода. Это позволяет мне просто автоматизировать обнаружение, какие классы могут быть сброшены. Я все еще могу сделать это, но метод не применяется или автоматически добавляется в IDE и полагается на надежду.
mattmanser

7
@ Крис, я не согласен, огромное перегиб. Это всегда признак языкового недостатка, когда больше архитектуры - это «лучшее» решение. Помните все паттерны, о которых никто больше не говорит, так как C # получил дженерики и анонимные методы?
Mattmanser

3
Разве вы не можете использовать «где T: IQueryable, T: IDoSomeStaticMath» или подобное?
Роджер Уиллкокс

2
@ ChrisMarasti-Georg: немного грязный, но интересный способ обойти эту маленькую конструкцию:, public abstract class DBObject<T> where T : DBObject<T>, new()а затем заставить все классы DB наследовать как DBObject<T>. Затем вы можете сделать извлечение по ключу статической функцией с типом возврата T в абстрактном суперклассе, заставить эту функцию создать новый объект T, а затем вызвать защищенный String GetRetrieveByKeyQuery()(определенный как абстрактный в суперклассе) этот объект для получения фактический запрос для выполнения. Хотя это может быть немного не по теме
Nyerguds

20

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

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

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

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

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


16

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

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

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

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

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


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

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

1
@Kramii: контракты для статических API. Я не хочу экземпляр объекта, просто гарантия конкретной подписи, например. IMatrixMultiplier или ICustomSerializer. Funcs / Actions / Delegates как члены класса делают свое дело, но IMO это иногда кажется излишним, и может привести в замешательство неопытного, пытающегося расширить API.
Дэвид Кучча

14

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

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


71
Извините .. Я не уверен, что это правильно! Интерфейс не определяет поведение. Интерфейс определяет набор именованных операций. Два класса могли бы реализовать метод интерфейса, чтобы вести себя совершенно по-разному. Итак, интерфейс не определяет поведение вообще. Класс, который реализует это делает.
Скотт Лэнгхэм

1
Надеюсь, ты не думаешь, что я привередлива ... но я думаю, что это важно для любого, кто изучает ОО, чтобы понять.
Скотт Лэнгхэм

4
Интерфейс должен указывать контракт, который включает в себя поведение и представление. Вот почему изменение поведения вызова интерфейса - нет-нет, так как оба должны быть исправлены. Если у вас есть интерфейс, где вызов действует по-другому (т.е. IList.Add сделал удаление), это было бы неправильно.
Джефф Йейтс

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

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

14

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

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


3
Я полностью согласен с этим простым, но эффективным ответом. Что было бы интересно в «статическом интерфейсе», так это то, что он представлял бы контракт. Может быть, это не следует называть «статическим интерфейсом», но мы все еще пропускаем конструкт. Например, проверьте официальный документ .NET об интерфейсе ICustomMarshaler. Требуется класс, который реализует его, чтобы «добавить статический метод с именем GetInstance, который принимает String в качестве параметра и имеет возвращаемый тип ICustomMarshaler». Это серьезно выглядит как определение «статического интерфейса» на простом английском языке, в то время как я бы предпочел его в C # ...
Саймон Мурье

@SimonMourier Эта документация могла быть написана более четко, но вы ее неправильно истолковали. Это не интерфейс ICustomMarshaler, который требует статического метода GetInstance. Это требует атрибут кода [MarshalAs]. Они используют фабричный шаблон, чтобы позволить атрибуту получить экземпляр присоединенного маршалера. К сожалению, они полностью забыли включить документирование требования GetInstance на странице документации MarshalAs (она показывает только примеры с использованием встроенных реализаций маршалинга).
Скотт Гартнер,

@ ScottGartner - Не поймите свою точку зрения. msdn.microsoft.com/en-us/library/… четко заявляет: «В дополнение к реализации интерфейса ICustomMarshaler пользовательские маршалеры должны реализовать статический метод с именем GetInstance, который принимает String в качестве параметра и имеет возвращаемый тип ICustomMarshaler. Это статический метод вызывается уровнем взаимодействия COM среды общего языка для создания экземпляра пользовательского маршалера. " Это определенно статичное определение контракта.
Саймон Мурье,

9

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

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


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;  

1
Другие языки (например, Java) позволяют вызывать статические методы из экземпляров объектов, хотя вы получите предупреждение о том, что вы должны вызывать их из статического контекста.
Крис Марасти-Георг

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

1
@CharlesBretana в случае универсальных типов, передаваемых типом. Я вижу много полезного в наличии интерфейсов для статических методов (или, если хотите, давайте назовем их «статическими интерфейсами» и позволим классу определять оба статических интерфейса и экземпляры интерфейсов). Так что, если у меня есть, Whatever<T>() where T:IMyStaticInterfaceя мог бы позвонить Whatever<MyClass>()и иметь T.MyStaticMethod()внутри Whatever<T>()реализации без необходимости экземпляра. Метод для вызова будет определен во время выполнения. Вы можете сделать это с помощью рефлексии, но нет никакого «контракта», который нужно навязывать.
Jcl

4

Что касается статических методов, используемых в неуниверсальных контекстах, я согласен, что не имеет смысла разрешать их в интерфейсах, так как вы не сможете вызывать их, если у вас все равно есть ссылка на интерфейс. Однако есть фундаментальная дыра в дизайне языка, созданная с использованием интерфейсов НЕ в полиморфном контексте, а в общем. В этом случае интерфейс - это вовсе не интерфейс, а скорее ограничение. Поскольку 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 решит эту проблему.


Операторы в C # являются статическими, поэтому это все еще не имеет смысла.
Джон Сондерс

С этой точки зрения ограничение проверяет, что TYPE (не экземпляр) имеет оператор + (и, соответственно, оператор + =), и позволяет генерировать шаблон, а затем, когда происходит фактическая замена шаблона, используемый объект гарантированно будет типа, который имеет этот оператор (или другой статический метод). Таким образом, вы можете сказать: SumElements <int> (0, 5); который бы создавал это: SumElements (int initVal, int [] values) {foreach (var v в значениях) {initVal + = v; }}, что, конечно, имеет смысл
Джереми Соренсен

1
Помните, что это не шаблон. Это дженерики, которые не совпадают с шаблонами C ++.
Джон Сондерс

@JohnSaunders: Обобщения не являются шаблонами, и я не знаю, как их можно было бы разумно сделать для работы со статически связанными операторами, но в общем контексте есть много случаев, когда было бы полезно указать, что a Tдолжно иметь статическое член (например, фабрика, которая производит Tэкземпляры). Даже без изменений во время выполнения, я думаю, что было бы возможно определить соглашения и вспомогательные методы, которые позволили бы языкам реализовать такую ​​вещь как синтаксический сахар эффективным и совместимым образом. Если бы для каждого интерфейса с виртуальными статическими методами существовал вспомогательный класс ...
суперкат

1
@JohnSaunders: Проблема не в том, что метод реализует интерфейс, а в том, что компилятор не может выбрать виртуальный метод для вызова, не имея экземпляра объекта, на котором основывается выбор. Решение состоит в том, чтобы отправлять вызов «статического интерфейса», не используя виртуальную диспетчеризацию (которая не будет работать), а вместо этого используя вызов универсального статического класса. Универсальная диспетчеризация на основе типов не требует наличия экземпляра, а просто наличия типа.
суперкат

3

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


3

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

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

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


Интересно, что в VB.Net вполне возможно вызывать статический метод для обоих экземпляров типа ad ad Instance (хотя в последнем случае IDE выдает предупреждение). Это не кажется проблемой. Вы можете быть правы насчет оптимизаций.
Крамии

3

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


Это интересное POV, и, вероятно, это то, что разработчики C # имели в виду. Теперь я буду думать о статических членах иначе.
Крами

3

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

При чтении из хранилища базы данных у нас есть универсальный класс 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 требуется только один раз для каждой таблицы для чтения, накладные расходы на создание еще одного экземпляра минимальны. Впрочем, было бы неплохо в этом случае здесь.


1

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


1

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

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

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


1
Да, статика в объявлении интерфейса была бы глупой. Класс не должен быть вынужден реализовывать интерфейс определенным образом. Однако ограничивает ли C # класс реализацией интерфейса с использованием нестатических методов. Имеет ли это смысл? Почему?
Крамии

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

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

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

1

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

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


1

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


2
Проблема в том, что этот вопрос не о "в ООП". Это про "в C #". В C # нет «сообщений».
Джон Сондерс

1

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

  static public bool IsHandled(XElement xml)

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

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

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

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

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


1

Тот факт, что статический класс реализован в 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);
}

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


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

1

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

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


0

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


0

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

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

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

public class DoubleWrapper: IZeroWrapper<double> {
  public double Zero { get { return 0; } }
}

0

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

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


0

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

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

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

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


-1

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.