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

Почему C # спроектирован таким образом?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Например:

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

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

  • Использовать reflection Уродливо и превосходит идею интерфейсов и polymorphismа.

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

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

  • Выполните активацию, а затем вызовите требуемый метод интерфейса

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

Пример:

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

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

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

 string DoSomething  () где T: ISomeFunction
 {
   если (T.someFunction ())
     ...
 }

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

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

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

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

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

Вероятно, близорукость.

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

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

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

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

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

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

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

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


 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; 

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

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

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

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

 constraint CHasPlusEquals { static CHasPlusEquals operator + (CHasPlusEquals a, CHasPlusEquals b); } T SumElements(T initVal, T[] values) where T : CHasPlusEquals { foreach (var v in values) { initVal += v; } } 

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

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

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

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

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

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

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

При чтении из хранилища базы данных у нас есть общий class DataTable, который обрабатывает чтение из таблицы любой структуры. Вся информация, относящаяся к таблице, помещается в один class для каждой таблицы, которая также содержит данные для одной строки из БД и которая должна реализовывать интерфейс 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 : List where T : IDataRow { public string GetDataStructure() // Desired: Static or Type method: // return (T.GetDataStructure()); // Required: Instantiate a new class: return (new T().GetDataStructure()); } } 

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

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

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

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

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

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

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

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

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

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

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

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

Currently, I write the static classes that contain these methods without any kind of checking to make sure that I haven’t forgotten anything. Is like the bad old days of programming before OOP.

C# and the CLR should support static methods in interfaces as Java does. The static modifier is part of a contract definition and does have meaning, specifically that the behavior and return value do not vary base on instance although it may still vary from call to call.

That said, I recommend that when you want to use a static method in an interface and cannot, use an annotation instead. You will get the functionality you are looking for.

I think the short answer is “because it is of zero usefulness”. To call an interface method, you need an instance of the type. From instance methods you can call any static methods you want to.

OK here is an example of needing a ‘type method’. I am creating one of a set of classes based on some source XML. So I have a

  static public bool IsHandled(XElement xml) 

function which is called in turn on each class.

The function should be static as otherwise we waste time creating inappropriate objects. As @Ian Boyde points out it could be done in a factory class, but this just adds complexity.

It would be nice to add it to the interface to force class implementors to implement it. This would not cause significant overhead – it is only a compile/link time check and does not affect the vtable.

However, it would also be a fairly minor improvement. As the method is static, I as the caller, must call it explicitly and so get an immediate compile error if it is not implemented. Allowing it to be specified on the interface would mean this error comes marginally earlier in the development cycle, but this is trivial compared to other broken-interface issues.

So it is a minor potential feature which on balance is probably best left out.

I think the question is getting at the fact that C# needs another keyword, for precisely this sort of situation. You want a method whose return value depends only on the type on which it is called. You can’t call it “static” if said type is unknown. But once the type becomes known, it will become static. “Unresolved static” is the idea — it’s not static yet, but once we know the receiving type, it will be. This is a perfectly good concept, which is why programmers keep asking for it. But it didn’t quite fit into the way the designers thought about the language.

Since it’s not available, I have taken to using non-static methods in the way shown below. Not exactly ideal, but I can’t see any approach that makes more sense, at least not for me.

 public interface IZeroWrapper { TNumber Zero {get;} } public class DoubleWrapper: IZeroWrapper { public double Zero { get { return 0; } } } 

As per Object oriented concept Interface implemented by classes and have contract to access these implemented function(or methods) using object.

So if you want to access Interface Contract methods you have to create object. It is always must that is not allowed in case of Static methods. Static classes ,method and variables never require objects and load in memory without creating object of that area(or class) or you can say do not require Object Creation.

When a class implements an interface,it is creating instance for the interface members. While a static type doesnt have an instance,there is no point in having static signatures in an interface.

  • В чем разница между динамическим и статическим polymorphismом в Java?
  • Как создать статический class в C ++?
  • Будет ли «пустой» конструктор или деструктор делать то же самое, что и сгенерированный?
  • Имеются ли свойства для записи только в практических приложениях?
  • Использование «окончательного» модификатора, когда это применимо в Java
  • Когда следует использовать «это» в classе?
  • Отправка сообщений между двумя объектами JPanel
  • «Закрытие - это объекты бедного человека и наоборот». Что это значит?
  • Что такое экземпляр Java?
  • Абстрактный class в Java
  • Должен ли я использовать свойства или прямую ссылку при доступе к переменным экземпляра внутри?
  • Давайте будем гением компьютера.