Почему C# не разрешает функции, не являющиеся членами, такие как C++


C# не позволит писать функции, не являющиеся членами, и каждый метод должен быть частью класса. Я думал, что это ограничение на всех языках CLI. Но я был неправ, и я обнаружил, что C++/CLI поддерживает функции, не являющиеся членами. Когда он компилируется, компилятор сделает метод членом некоторого неназванного класса.

вот что говорит стандарт C++ / CLI,

[Примечание: функции, не являющиеся членами, обрабатываются CLI как члены некоторого неназванного класса; однако, в Исходный код C++/CLI, такие функции не могут быть определены явно с этим именем класса. конец Примечание]

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

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

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

какие-то мысли..?

11 60

11 ответов:

смотрите эту запись в блоге:

http://blogs.msdn.com/ericlippert/archive/2009/06/22/why-doesn-t-c-implement-top-level-methods.aspx

(...)

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

Я понимаю, что такой общий ответ, вероятно, не касается конкретного вопроса.

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

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

(...)

и это последующее сообщение:

http://blogs.msdn.com/ericlippert/archive/2009/06/24/it-already-is-a-scripting-language.aspx

(...)

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

(курсив из оригинала)

C# не позволяет этого, потому что Java этого не позволял.

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

  • Java был разработан, чтобы быть простой. Они попытались сделать язык без случайных ярлыков, так что у вас обычно есть только один простой способ сделать все, даже если другие подходы были бы чище или более краткими. Они хотели минимизировать кривую обучения, и обучение "класс может содержать методы" является проще ,чем"класс может содержать методы, а функции могут существовать вне классов".
  • внешне он выглядит менее объектно-ориентированным. (Все, что не является частью объекта, очевидно, не может быть объектно-ориентированным? Может ли это быть? конечно, C++ говорит Да, но C++ не был вовлечен в это решение)

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

В C++, где разрешены функции, не являющиеся членами, они часто предпочтительны по нескольким причинам:

  • это помогает инкапсуляции. Чем меньше методы имеют доступ к закрытым членам класса, тем легче этого класса будут рефакторить и поддерживать. Инкапсуляция является важной частью ООП.
  • код может быть использован гораздо легче, когда он не является частью класса. Например, стандарт C++ библиотека определяет std::find или std::sort` как функции-не члены, так что они могут быть повторно использованы на любом типе последовательностей, будь то массивы, наборы, связанные списки или (для std::find, по крайней мере) потоки. Повторное использование кода также является важной частью ООП.
  • это дает нам лучшую развязку. Элемент find функция не должна знать о классе LinkedList, чтобы иметь возможность работать над ним. Если бы он был определен как функция-член, он был бы членом класса LinkedList, в основном слияние двух концепций в одну большую каплю.
  • расширяемость. Если вы признаете, что интерфейс класса-это не только "все его открытые члены", но и "все функции, не являющиеся членами, которые работают с классом", то становится возможным расширить интерфейс класса без необходимости редактировать или даже перекомпилировать сам класс.

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

на самом деле, C#, кажется, понял почти то же самое, гораздо позже. Как вы думаете, почему были добавлены методы расширения? Они являются попыткой достижения вышеуказанного, сохраняя при этом простой Java-подобный синтаксис. Лямбды также являются интересными примерами, поскольку они также являются по существу небольшими функциями, определенными свободно, а не как члены каких-либо конкретный класс. Так что ДА, концепция функций, не являющихся членами, полезна, и дизайнеры C#поняли то же самое. Они только что пытались протащить концепцию через заднюю дверь.

http://www.ddj.com/cpp/184401197 и http://www.gotw.ca/publications/mill02.htm это две статьи, написанные экспертами C++ по этому вопросу.

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

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

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

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

CLS (спецификация общего языка) говорит, что вы не должны иметь функции, не являющиеся членами библиотеки, которая соответствует CLS. Это как дополнительный набор ограничений в дополнение к основным ограничениям CLI (common language interface).

возможно, что будущая версия C# добавит возможность писать using директива, которая позволяет обращаться к статическим членам класса без квалификации имени класса:

using System.Linq.Enumerable; // Enumerable is a static class

...

IEnumerable<int> range = Range(1, 10); // finds Enumerable.Range

затем не нужно будет менять CLS и существующие библиотеки.

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

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

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

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

Я думаю, вам действительно нужно уточнить, что вы хотели бы создать статические методы, не являющиеся членами для достижения.

например, некоторые из вещей, которые вы могли бы хотеть, чтобы они могли быть обработаны с Методы Расширения

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

кроме того, вы не должны обязательно сравнивать объектную модель C++С C#. C++ в значительной степени (но не идеально) совместим с C, у которого вообще не было системы классов, поэтому C++ должен был поддерживать эту идиому программирования из наследия C, а не для какого - либо конкретного императива дизайна.

имейте в виду: C++ - это гораздо более сложный язык, чем C#. И хотя они могут быть похожи синтаксически, они очень разные животные семантически. Вы бы не подумали, что это будет ужасно трудно сделать такие изменения, но я видел, как это может быть. У ANTLR есть хорошая вики-страница под названием что затрудняет языковую проблему? это хорошо, чтобы проконсультироваться по таким вопросам, как это. В этом случае:

контекстно-зависимый лексер? Вы не могу решить, какой символ vocabulay соответствовать, если вы не знаете, какое предложение вы анализируете.

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

кроме того, если компилятор метода такой:

public void Foo()
{
    Bar();
}

...теперь он должен ответить на вопрос "находится ли бар в этом классе или это глобальный класс?"

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

Это еще одна вещь, которая вызывает проблемы. Помните, что C# не требует прямых объявлений. Компилятор сделает один проход только для того, чтобы определить, какие классы называются и какие функции эти классы содержат. Теперь вам придется беспокоиться о поиске классов и функции, где функции могут быть как внутри, так и вне класса. Это то, о чем парсер C++ не должен беспокоиться, поскольку он анализирует все порядок.

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

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

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

Если вы думаете об этом, контраргумент будет "почему C++ не поддерживает методы расширения" и ответ заключается в их цели.

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

в большинстве случаев я использовал функции пространства имен в C++ для создания функций, которые принимают объекты и выполняют функции, которые я не хочу помещать внутрь члена класса функция.

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

рассмотрим следующий код, например:

template<typename T>
unsigned int findCount(vector<T>& vec, T data){
    unsigned int count = 0;
    for(auto val : vec)
        if(val == data) ++count;
    return count;
}

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

В C# вы можете достичь той же цели с расширением метод:

static class Extensions {
    public static uint FindCount<T>(this List<T> list, T data) {
        uint counts = 0;
        foreach (var item in list)
            if (item.Equals(data)) ++counts;
        return counts;
    }
}

и оба они работают одинаково что круто. Многие будут спорить о пропущенных методах расширения C++, многие будут спорить о пропущенных функциях пространства имен C#.

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

Как вы процитировали в своем вопросе, C++ / CLI не "правда" элемент пространства имен поддержки функции, поскольку он добавляет неназванный класс для их использования, который никоим образом не близок к реализации C++, может быть, близок к тому, как он выглядит, но действительно отличается.

в конце концов, функциональность-это всегда то, что нужно с нетерпением ждать, и насколько я хочу, чтобы функции-члены пространства имен в C# были такими же, как и методы расширения в C++. А это не так уж много.