Хобрук: Ваш путь к мастерству в программировании

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

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

    ....

 }
03.11.2008

  • Что ж, в Java 8 он есть (stackoverflow.com/questions/ 23148471 /). 11.07.2015
  • Посмотрите, как можно объединить статическое поведение с наследованием или реализацией интерфейса: stackoverflow.com/a/13567309/880990 30.07.2016
  • IListItem.ScreenName() => ScreenName() (с использованием синтаксиса C # 7) явно реализует метод интерфейса, вызывая статический метод. Однако все становится уродливым, когда вы добавляете к этому наследование (вам нужно переопределить интерфейс) 18.04.2017
  • Просто сообщаю всем, что ожидание подошло к концу! В C # 8.0 есть методы статического интерфейса: dotnetfiddle.net/Lrzy6y (хотя они работают немного иначе, чем их хотел OP для работы - не обязательно их реализовывать) 21.11.2019

Ответы:


1

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

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

03.11.2008
  • Прекрасный пример! Но я не уверен, что понимаю ваши рассуждения. Конечно же, компилятор мог быть разработан так, чтобы смотреть и на члены statuc? Разве у экземпляров нет таблицы адресов для реализации этих методов? Нельзя ли включить в эту таблицу статические методы? 03.11.2008
  • Я понял, когда собирался прояснить, что то, о чем вы просите, безусловно, может быть реализовано компилятором. Итак, я изменил свой ответ на лучший ответ Почему? 03.11.2008
  • Есть случай, когда это может пригодиться. Например, я хочу, чтобы все разработчики реализовали метод GetInstance, который принимает аргумент XElement. Я не могу ни определить это как статический метод в интерфейсе, ни требовать подпись конструктора от интерфейса. 16.02.2011
  • Если у вас есть экземпляр класса, у вас может быть нестатический метод, который выполняет то, что вы указываете. Если у вас нет экземпляра ... как узнать, какую реализацию использовать? 22.02.2011
  • Многие люди взвесили обе стороны: от «Это не имеет смысла» до «Это ошибка, я бы хотел, чтобы вы могли это сделать». (Я думаю, что для него есть допустимые варианты использования, именно так я и оказался здесь.) В качестве обходного пути метод экземпляра может просто делегировать статическому методу. 31.03.2011
  • Вы также можете просто реализовать этот фрагмент как метод расширения в базовом интерфейсе, например: public static object MethodName(this IBaseClass base) внутри статического класса. Обратной стороной, однако, является то, что в отличие от наследования интерфейса - это не заставляет / не позволяет отдельным наследникам хорошо переопределять методологию. 03.11.2012
  • Это имело бы смысл с дженериками. Например, void Something ‹T› (), где T: ISomeInterface {new T (). DoSomething1 (); T.DoSomething2 (); } 18.12.2013
  • Привет. Я приезжаю через два года, чтобы спросить об этом: методы, указанные в интерфейсе, должны быть там, чтобы указать контракт для взаимодействия с объектом. Это почему? Почему взаимодействовать только с объектом? Если это правда, то рассуждения C # прекрасны, но почему это правда? 29.10.2015
  • @harpo: Было бы полезно, если бы .NET могла разрешить расширение существующих интерфейсов новыми членами, при этом система автоматически генерирует для любого отсутствующего члена реализации простой метод по умолчанию, который привязывается к одному из двух статических методов с подходящими именами и тегами. в интерфейсе (если они существуют) с дополнительным первым параметром типа T для реализаций классов или ref T для реализаций структур. Эффективность некоторых интерфейсов, таких как IEnumerable<T>, можно значительно повысить, добавив такие элементы, как int Move(int), чтобы перемещать более одного элемента за раз. 01.09.2016
  • @supercat вы описываете методы расширения? 13.09.2016
  • @ ChrisMarasti-Georg: Методы расширения - это синтаксический сахар времени компиляции; что-то вроде SomeEnumerable.Count() превращается в Enumerator.Count(SomeIEnumerable), если компилятор знает о последнем методе расширения. То, что я описываю, позволило бы добавить что-то вроде bool Move(ref int) в качестве фактического члена IEnumerable<T>, при этом среда выполнения .NET использует атрибуты, прикрепленные к этому интерфейсу, для автоматического создания реализации типа bool Move(ref int n) { return IEnumerable<T>.Move(this, ref n); };. Классы, которые могут поддерживать быстрый переход по N (как _6 _... 13.09.2016
  • ... вероятно, будет) мог бы обработать перемещение N-элементов за один шаг, но те, которые не запрограммированы для этой способности, могут быть обработаны статическим методом, который просто вызовет MoveNext соответствующее количество раз. Обратите внимание, что если кто-то использовал Append для соединения переданного 1 000 000 элементов List<T> с итератором из 12 элементов, попытка пропустить 1 000 0005 элементов могла бы быть эффективно обработана путем создания перечислителя List<T> с просьбой переместить 1 000 005 шагов, найдя выяснилось, что не хватило 5 элементов, а затем попросил итератор продвинуть 5 элементов. Намного лучше, чем звонить ... 13.09.2016
  • ..._ 1_ на List<T>.Enumerator 1000000 раз. 13.09.2016

  • 2

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

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

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

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

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

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

    03.11.2008
  • Отлично, это ответ, который я хотел написать - я просто не знал деталей реализации. 03.11.2008
  • Отличный ответ. И мне нужен этот «типовой метод»! Было бы полезно во многих случаях (подумайте о метаданных для типа / класса). 18.04.2012
  • Это правильный ответ. Вы передаете кому-то интерфейс, они должны знать, как вызывать метод. Интерфейс - это просто таблица виртуальных методов. В вашем статическом классе этого нет. Вызывающий не знал бы как вызвать метод. (До того, как я прочитал этот ответ, я думал, что C # просто педантичен. Теперь я понимаю, что это техническое ограничение, наложенное тем, что такое интерфейс ). Другие люди будут говорить с вами свысока о том, что это плохой дизайн. Это неплохой дизайн - это техническое ограничение. 11.06.2012
  • В C # - нельзя ли это реализовать как: public static object Method<T>(this T t)? Или я забываю, что существует определенное ограничение C # на этот метод расширения? 03.11.2012
  • +1 за то, что действительно ответил на вопрос, а не за педантизм и сопливость. Как сказал Ян Бойд: «Это неплохой дизайн - это техническое ограничение. 30.03.2013
  • Так же, как теоретическая мысль: почему компилятор не может неявно сгенерировать класс (с vtable!) Для каждого типа, который статически реализует интерфейс, пусть методы этого класса перенаправляют все вызовы статическим методам и вставляют экземпляр этого класса всякий раз, когда тип приводится к типу интерфейса (например, назначается переменной этого типа интерфейса)? 30.01.2014
  • Кроме того, я не уверен, что вижу полное обоснование в первой цитате: когда методы вызываются как статические методы (class.method), метод is по-прежнему разрешается исключительно путем статического анализа кода. Разве эта тема не о том, что вызывается при вызове методов interface, которые обычно не точно известны во время компиляции? 30.01.2014
  • Вполне возможно сгенерировать объект для статического класса со связанной vtable. Посмотрите, как Scala обрабатывает object и как им разрешено реализовывать интерфейсы. 08.05.2014
  • @Sebastian - я подхожу к опасной близости к тому, чтобы превзойти мои знания о компиляторе, но поскольку C # использует этот термин (в соответствии с другими языками на основе C), определение статического метода отправляется статически. Это исключает vtable (которая будет динамически отправляться). Объекты Scala на самом деле не статичны - это просто обычный класс, реализованный как синглтон (с vtable и связанными с ней плюсами и минусами). 09.05.2014
  • Таблица vtable может просто указывать на статическую реализацию, как это делает Func<>, но вместо одного указателя она содержит все указатели на методы, требуемые интерфейсом. Использование static, как в статической диспетчеризации, искусственно ограничивает язык ради «объектной ориентации». 10.05.2014
  • Большинство объектно-ориентированных языков имеют тенденцию быть слишком строгими по отношению к диспетчеризации: если тип объекта во время выполнения известен, то нет абсолютно никакого смысла извлекать фактический метод через доступ к vtable (это сделало бы дешевую оптимизацию компилятора). С другой стороны, утверждение, что статически отправляемый метод (либо static, либо просто не virtual) не может удовлетворить интерфейс, в равной степени бессмысленно, потому что для удовлетворения интерфейса все, что требуется, - это vtable для сопоставления с реализуемыми методами. Вот где все ОО-языки типа C идут не так. Думаю, оглядываясь назад, легко сказать. / rant 10.05.2014
  • tldr; статическая и динамическая отправка не должна зависеть от объявления метода, а от использования, как это сделано в классах типов Go, Rust и Haskell. 10.05.2014
  • с полиморфным экземпляром должна быть возможность определить контракт статического типа параметрического типа-параметра, а затем вызвать эти статические методы. 24.10.2014
  • @IanBoyd Я думаю, что «плохой дизайн» здесь - это то, что вызвало техническое ограничение. Другими словами, если бы фреймворк был спроектирован иначе, этого технического ограничения не существовало бы. 30.12.2014
  • Я хочу дать обещание, что у класса будет такая константа, как Zero или NaN. Чтобы получить это, мне понадобятся статические члены в интерфейсах, а также способ ссылки на реализующий тип в интерфейсе, которого тоже не существует. 19.03.2015
  • Все эти разговоры о плохом дизайне / технических ограничениях как бы упускают суть ... очевидно, что ваше определение static не совпадает с определением static командой компиляторов C #. Очевидно, что можно создать то, о чем вы просите (как указывает Липперт и другие на разных языках), но мы бы не стали называть их статическими методами, если бы команда компиляторов C # не изменила их определение. 19.03.2015
  • Очевидным вариантом использования является отделение от конкретного класса для поддержки DI и тестирования статической реализации. Я думаю, что случай метода типа, о котором вы говорите, удовлетворит эту потребность, если бы он был реализован на C #. Я полагаю, что в большинстве случаев разработчики не так озабочены внутренней работой vtable или разрешением детерминированных методов (да и не должно быть в большинстве случаев), но имеют потребность более высокого порядка - или, по крайней мере, предполагают, что они это делают. В любом случае, ваш +1 от меня, по большей части, за введение термина «чудак компилятора». 07.08.2016

  • 3

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

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

    Например:

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

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

    30.10.2011
  • +1 за Большинство ответов здесь, кажется, упускают из виду главное .; невероятно, как кажется, что почти все ответы уклоняются от сути вопроса, чтобы углубиться в в основном бесполезное словоблудие ... 31.05.2012
  • Ваши примеры не имеют для меня смысла ... функциональность IsQueryable может быть аннотацией / атрибутом или, еще лучше, интерфейсом, который реализует класс (в противном случае, как вы его запрашиваете?). RowCount ... чего? Количество клиентов? Это то, что должен делать репозиторий клиентов, а не класс клиентов. Пример статической математики неполный, поэтому я не уверен, что вы на самом деле пытаетесь там сделать. 23.10.2012
  • @Chris Вот конкретный пример, который заставил меня снова столкнуться с этим ограничением. Я хочу добавить интерфейс IResettable к классам, чтобы указать, что они кэшируют определенные данные в статических переменных, которые могут быть сброшены администратором сайта (например, список категорий заказа, набор ставок НДС, список категорий, полученных из внешнего API) для уменьшения обращений к БД и внешнему API, очевидно, потребуется сброс статического метода. Это позволяет мне просто автоматизировать определение классов, которые можно сбросить. Я все еще могу это сделать, но этот метод не применяется и не добавляется автоматически в IDE и полагается на надежду. 26.10.2012
  • @mattmanser Я бы сказал, что вы достигли точки в своей архитектуре, когда вам нужно использовать фактический кеш, а не статические переменные в объектах домена (решение, которое я бы тоже поставил под сомнение, но это другое обсуждение :)). 26.10.2012
  • @Chris Я не согласен, массовое убийство. Когда больше архитектуры - «лучшее» решение, это всегда признак языковой ошибки. Помните все шаблоны, о которых больше никто не говорит с тех пор, как в C # появились универсальные шаблоны и анонимные методы? 30.10.2012
  • Разве вы не можете использовать where T: IQueryable, T: IDoSomeStaticMath или аналогичный? 07.12.2012
  • @ ChrisMarasti-Georg: Классический пример - это типизированные классы баз данных, все из которых имеют функцию извлечения по ключу. Customer cust = Customer.Retrieve(233);. Эти классы базы данных часто наследуют один тип класса базы данных и часто имеют общие свойства, но вы никогда не можете определить / вызвать их функцию (ы) Retrieve как общую вещь, потому что она статична, несмотря на то, что она у всех есть. 06.05.2015
  • @Nyerguds Верно, это было бы неплохим вариантом использования. У вас может быть что-то вроде T Retrieve<T: BaseDbClass>(long id) на BaseDbClass, что, я думаю, все еще довольно интуитивно понятно. 06.05.2015
  • @ ChrisMarasti-Georg: Несколько грязный, но интересный способ обойти это небольшая конструкция: public abstract class DBObject<T> where T : DBObject<T>, new(), а затем сделать наследование всех классов DB как DBObject<T>. Затем вы можете сделать извлечение по ключу статической функцией с типом возврата T в абстрактном суперклассе, заставить эту функцию создать новый объект T, а затем вызвать защищенный String GetRetrieveByKeyQuery() (определенный как абстрактный в суперклассе) для этого объекта, чтобы получить фактический запрос для выполнения. Хотя это может быть немного не по теме 12.05.2015
  • @Nyerguds, это может сработать, но это ужасный хак create an instance using reflection (new() constraint does) and fill with method. Create an instance не равно create an empty dummy instance and then fill it 29.12.2015
  • @AlexZhukovskiy: Я думаю, вы неправильно поняли конструкцию. Он не задействует какие-либо фиктивные объекты. У меня есть суперкласс со статическими методами, которые возвращают универсальный тип. Подкласс жестко кодирует себя как быть этим типом, поэтому при вызове в подклассе эти статические методы могут создавать объекты этого типа и возвращать их как этот тип. 12.01.2016
  • @Nyerguds Я думаю, должна быть какая-то возможность разрешить интерфейсам иметь статические методы, поэтому тип, который наследует этот интерфейс, должен иметь специальный статический метод. Это может быть полезно для любых методов, таких как T Parse(string source); 13.01.2016
  • @AlexZhukovskiy Как я показал, он работает с абстрактными функциями с дженериками. Но интерфейсы разработаны, чтобы позволить функциям обрабатывать объекты разных типов на основе их общих свойств и функций. C # не поддерживает обработку фактических типов так же, как экземпляры объектов. Любые вещи, сделанные подобным образом на уровне классов, вероятно, будут представлять собой беспорядок в коде отражения. 13.01.2016
  • @Nyerguds, теперь это правда, но именно поэтому я так говорю. Если бы эта функция была включена в спецификации CIL при ее разработке, это было бы намного проще. Компилятору просто нужно, чтобы у определенного класса был какой-то метод. Мы всегда можем создать экземпляр объекта и вызвать метод интерфейса Parse, но это не то же самое, что создать его из статического метода, потому что класс может иметь поля только для чтения, которые могут быть инициализированы только во время создания. 14.01.2016

  • 4

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

    string DoSomething<T>() where T:ISomeFunction
    {
      if (T.someFunction())
        ...
    }
    

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

    1. Create a static generic class whose type parameter will be the type you'd be passing to DoSomething above. Each variation of this class will have one or more static members holding stuff related to that type. This information could supplied either by having each class of interest call a "register information" routine, or by using Reflection to get the information when the class variation's static constructor is run. I believe the latter approach is used by things like Comparer<T>.Default().
    2. For each class T of interest, define a class or struct which implements IGetWhateverClassInfo<T> and satisfies a "new" constraint. The class won't actually contain any fields, but will have a static property which returns a static field with the type information. Pass the type of that class or struct to the generic routine in question, which will be able to create an instance and use it to get information about the other class. If you use a class for this purpose, you should probably define a static generic class as indicated above, to avoid having to construct a new descriptor-object instance each time. If you use a struct, instantiation cost should be nil, but every different struct type would require a different expansion of the DoSomething routine.

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

    17.11.2011

    5

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

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

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

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

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

    03.11.2008
  • Я впервые столкнулся с проблемой в контексте дженериков, но мне интересно, может ли включение статических методов в интерфейсы быть полезным и в других контекстах? Есть ли причина, по которой ничего нельзя изменить? 03.11.2008
  • я тоже сталкиваюсь с этим при реализации универсального класса, который требует, чтобы тип параметра создавался сам с некоторыми параметрами. Поскольку new () не может принимать никаких. Ты уже придумал, как это сделать, Крами? 09.12.2009
  • @Kramii: контракты на статические API. Мне не нужен экземпляр объекта, просто гарантия конкретной подписи, например. IMatrixMultiplier или ICustomSerializer. Funcs / Actions / Delegates как члены класса делают трюк, но IMO это иногда кажется излишним и может сбивать с толку неопытных, пытающихся расширить API. 10.05.2010

  • 6

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

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

    22.08.2012
  • Я полностью согласен с этим простым, но эффективным ответом. Что было бы интересно в статическом интерфейсе, так это то, что он представлял бы контракт. Возможно, это не следует называть статическим интерфейсом, но мы все равно пропускаем конструкцию. Например, посмотрите официальный документ .NET об интерфейсе ICustomMarshaler. Он требует, чтобы класс, реализующий его, добавил статический метод с именем GetInstance, который принимает String в качестве параметра и имеет тип возврата ICustomMarshaler. Это действительно похоже на определение статического интерфейса на простом английском языке, в то время как я бы предпочел его на C # ... 27.10.2015
  • @SimonMourier Эту документацию можно было бы написать более четко, но вы неправильно ее истолковываете. Статический метод GetInstance требуется не для интерфейса ICustomMarshaler. Этого требует атрибут кода [MarshalAs]. Они используют фабричный шаблон, чтобы атрибут мог получить экземпляр присоединенного упаковщика. К сожалению, они полностью забыли включить документирование требования GetInstance на страницу документации MarshalAs (на ней показаны только примеры с использованием встроенных реализаций маршалинга). 20.06.2017
  • @ScottGartner - Не понимаю. msdn.microsoft.com/en-us/library/ четко сказано: помимо реализации интерфейса ICustomMarshaler, настраиваемые маршалеры должны реализовывать статический метод GetInstance, который принимает String в качестве параметра и имеет тип возвращаемого значения ICustomMarshaler. Этот статический метод вызывается уровнем взаимодействия COM среды CLR для создания экземпляра настраиваемого упаковщика. Это определенно статическое определение контракта. 20.06.2017

  • 7

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

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

    03.11.2008
  • Извините .. Я не уверен, что это правильно! Интерфейс не определяет поведение. Интерфейс определяет набор именованных операций. Два класса могут реализовать метод интерфейса, чтобы вести себя совершенно по-разному. Итак, интерфейс вообще не определяет поведение. Класс, реализующий это, делает. 03.11.2008
  • Надеюсь, вы не думаете, что я разборчив ... но я думаю, что это важное различие для понимания любого, кто изучает объектно-ориентированный подход. 03.11.2008
  • Интерфейс должен определять контракт, который включает поведение и представление. Вот почему изменение поведения вызова интерфейса недопустимо, поскольку оба должны быть исправлены. Если у вас есть интерфейс, в котором вызов действует по-другому (например, IList.Add выполнил удаление), это было бы неправильно. 03.11.2008
  • Что ж, да, вам бы пришлось исказить голову, чтобы определить метод, который не соответствовал бы его названию. Однако, если у вас есть IAlertService.GetAssistance (), его поведение может заключаться в том, чтобы мигать светом, подавать сигнал тревоги или тыкать палкой в ​​глаз. 04.11.2008
  • Реализация также сможет записывать в файл журнала. Это поведение не указано в интерфейсе. Но, может быть, ты прав. Поведение «получить помощь» действительно следует уважать. 04.11.2008
  • Рассмотрим это с точки зрения языковой независимости: интерфейсы - это абстрактные типы. Типы - это то, что описывает экземпляр или значение. Статические классы не являются истинными типами, говоря теоретически, поскольку они не могут быть созданы. Из этого следует, что статические классы должны / не могут иметь интерфейсы. 07.11.2012
  • @ScottLangham Я смотрю на эти названные операции как на поведение. То, как класс реализует это поведение, не имеет значения. В приведенном выше примере GetAssistance () - это поведение. Как это происходит, для меня не имеет значения; и в этом все дело. Классы бесполезны без поведения; если только они не являются простыми пакетами данных. 25.02.2013
  • Это старый ответ на старый вопрос, но вот почему я проголосовал против: если бы интерфейсы могли обеспечивать определенное поведение, они были бы не столь полезны. Одна из наиболее частых причин использования интерфейсов - это позволить нескольким дочерним классам реализовать различное поведение для одного и того же контракта (например, interface IStore = ›для памяти, для диска, для базы данных, для NULL и т. Д.). Разработчик интерфейса может (и должен) задокументировать ожидаемое поведение и, по крайней мере, предположения в комментариях к документу, но интерфейс не может обеспечить его соблюдение. В частности, в C # нельзя даже ограничивать исключения. 20.06.2017
  • Как и большинство людей, которые на протяжении многих лет голосовали против этого, вы путаете поведение с реализацией. Если использовать пример с глупым животным / собакой, Лай - это поведение. Интерфейс обеспечивает поведение Bark, а не реализацию такого поведения. Контракт / интерфейс определяет поведение. Интерфейсы не позволяют реализовать различное поведение, они позволяют реализовать поведение по-разному. Лай есть лай независимо от породы. Здесь все семантика и педантика. 12.07.2017

  • 8

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

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


    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;  
    
    03.11.2008
  • Другие языки (например, Java) позволяют вызывать статические методы из экземпляров объектов, хотя вы получите предупреждение о том, что их следует вызывать из статического контекста. 03.11.2008
  • Разрешение вызова статического метода из экземпляра также разрешено в .Net. Это другое дело. Если статический метод реализует интерфейс, вы можете вызвать его БЕЗ экземпляра. ЭТО то, что не имеет смысла. Помните, что вы не можете поместить реализацию в интерфейс, она должна быть в классе. Итак, если бы для реализации этого интерфейса были определены пять разных классов, каждый из которых имел бы свою реализацию этого статического метода, что бы использовал компилятор? 30.12.2014
  • @CharlesBretana в случае дженериков - тот, который соответствует переданному типу. Я вижу большую пользу в наличии интерфейсов для статических методов (или, если хотите, назовем их статическими интерфейсами и позволим классу определять как статические интерфейсы, так и экземпляр интерфейсы). Итак, если у меня есть Whatever<T>() where T:IMyStaticInterface, я мог бы вызвать Whatever<MyClass>() и иметь T.MyStaticMethod() внутри реализации Whatever<T>() без необходимости в экземпляре. Метод для вызова будет определен во время выполнения. Вы можете сделать это с помощью отражения, но принудительного заключения контракта нет. 03.03.2015

  • 9

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

    13.08.2013
  • Операторы в C # статичны, поэтому в этом нет смысла. 13.08.2013
  • В этом суть, ограничение проверяет, что ТИП (не экземпляр) имеет оператор + (и, как расширение, оператор + =) и позволяет сгенерировать шаблон, а затем, когда происходит фактическая подстановка шаблона, используемый объект гарантированно будет типа, который имеет этот оператор (или другой статический метод). Итак, вы можете сказать: SumElements ‹int› (0, 5); который будет создавать это: SumElements (int initVal, int [] values) {foreach (var v in values) {initVal + = v; }}, что, конечно, имеет смысл 14.08.2013
  • Помните, что это не шаблон. Это универсальные шаблоны, которые отличаются от шаблонов C ++. 14.08.2013
  • @JohnSaunders: универсальные шаблоны не являются шаблонами, и я не знаю, как их можно разумно заставить работать со статически привязанными операторами, но в общих контекстах есть много случаев, когда может быть полезно указать, что T должен иметь статический член (например, фабрика, производящая T экземпляра). Я думаю, что даже без изменений среды выполнения можно было бы определить соглашения и вспомогательные методы, которые позволили бы языкам реализовать такую ​​вещь, как синтаксический сахар, эффективным и совместимым образом. Если бы для каждого интерфейса с виртуальными статическими методами был вспомогательный класс ... 14.12.2013
  • ... тогда, если T ограничен значением IFoo<T>, T.CreateThing(paramString) станет IFoo_Helper<T>.CreateThing(paramString), а в классе Fred, который реализует IFoo<Fred>, определение static Fred IFoo.CreateThing(string st) пометит метод так, чтобы метод IFoo_Helper мог найти его через Reflection в первый раз, когда он понадобится [будущее доступ будет через кэшированного делегата]. 14.12.2013
  • @supercat: Мне кажется, я видел несколько подобных примеров. Единственное, что мешает определенному методу static, - это то, что он реализует член интерфейса. 14.12.2013
  • @JohnSaunders: Проблема не столько в том, что метод реализует интерфейс, сколько в том, что компилятор не может выбрать виртуальный метод для вызова, не имея экземпляра объекта, на котором основывается выбор. Решение состоит в том, чтобы отправлять вызов статического интерфейса без использования виртуальной диспетчеризации (которая не будет работать), а вместо этого использовать вызов общего статического класса. Универсальная диспетчеризация на основе типов не требует наличия экземпляра, а просто наличия типа. 15.12.2013

  • 10

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

    03.11.2008

    11

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

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

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

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

  • 12

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

    03.11.2008
  • Это интересная точка зрения, и, вероятно, именно это и имели в виду дизайнеры C #. С этого момента я буду думать о статических членах по-другому. 03.11.2008

  • 13

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

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

    26.02.2009

    14

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

    03.11.2008

    15

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

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

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

    03.11.2008
  • Да, статика в объявлении интерфейса была бы глупой. Класс не должен быть принужден к реализации интерфейса определенным образом. Однако ограничивает ли C # класс реализацию интерфейса с использованием нестатических методов. Имеет ли это смысл? Почему? 03.11.2008
  • Вы не можете использовать ключевое слово static. Однако нет никаких ограничений, потому что вам не нужно ключевое слово static для написания метода, который ведет себя статически. Просто напишите метод, который не обращается ни к одному из членов своего объекта, тогда он будет вести себя так же, как статический метод. Итак, ограничение есть. 03.11.2008
  • Верный Скотт, но он не позволяет кому-либо получить статический доступ к этому методу в другой части кода. Не то, чтобы я мог придумать причину, по которой вы хотели бы, но, похоже, это то, о чем спрашивает OP. 03.11.2008
  • Что ж, если вы действительно почувствовали необходимость, вы могли бы написать его как статический метод для доступа в другой части кода и просто написать нестатический метод для вызова статического метода. Я могу ошибаться, но сомневаюсь, что это цель вопрошающего. 04.11.2008

  • 16

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

    У меня было несколько классов статического бизнес-уровня, в которых реализованы методы CRUD, такие как «Create», «Read», «Update», «Delete» для каждого типа сущности, такого как «User», «Team» и т. Д. Затем я создал базу элемент управления, имеющий абстрактное свойство для класса бизнес-уровня, реализующего методы CRUD. Это позволило мне автоматизировать операции «Создать», «Прочитать», «Обновить», «Удалить» из базового класса. Мне пришлось использовать Singleton из-за статического ограничения.

    19.01.2011

    17

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

    15.07.2013
  • Проблема в том, что этот вопрос не касается ООП. Речь идет о C #. В C # сообщений нет. 14.12.2013

  • 18

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

      static public bool IsHandled(XElement xml)
    

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

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

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

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

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

    29.07.2013

    19

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

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

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

  • 20

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

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

    18.01.2016

    21

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

    03.11.2008

    22

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

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

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

    23

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

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

    02.07.2017

    24

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

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

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

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

    14.05.2019

    25

    Статические методы в интерфейсе разрешены начиная с C # 9 (см. https://www.dotnetcurry.com/csharp/simpler-code-with-csharp-9).

    14.07.2021

    26

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

    05.06.2018
    Новые материалы

    Управление DOM для чайников вроде меня
    Одной из первых вещей, которую мы рассмотрели, когда начали изучать Javascript во Flatiron, была модель DOM. Кто он? Чем он занимается? Он больше машина, чем человек? Ну да довольно много. ДОМ..

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

    мои январские чтения по программированию
    Эрик Эллиот Программирование приложения JavaScript Эл Свейгарт «Автоматизируйте скучные вещи с помощью Python» Прогрессивное веб-приложение Google..

    Создание ассоциаций секвелизации с помощью инструмента командной строки Sequelize
    Sequelize - популярный, простой в использовании инструмент объектно-реляционного сопоставления (ORM) JavaScript, который работает с базами данных SQL. Довольно просто начать новый проект с..

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

    Введение в машинное обучение для обнаружения аномалий (часть 1)
    Тщательно созданный, тщательно спроектированный ресурс для специалистов по данным. Часть 1 Главы 03 из Руководства по машинному обучению для обнаружения аномалий Внимание! Прежде чем вы..

    Начало работы с Pulumi в Digital Ocean
    Цифровой океан (ДО) — отличная альтернатива многим другим поставщикам облачных услуг. DO предоставляет простой и понятный пользовательский интерфейс, упрощающий управление инфраструктурой и..