Почему С# не разрешает функции, отличные от членов, такие как С++

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

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

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

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

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

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

Любые мысли..?

Ответ 1

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

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

(...)

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

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

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

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

(...)

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

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

(...)

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

(выделение из исходного текста)

Ответ 2

С# не разрешает его, потому что Java не разрешает его.

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

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

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

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

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

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

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

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

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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

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

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

...

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

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

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

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

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

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

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

Ответ 9

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

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

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

Кроме того, если компилятор сталкивается с таким способом:

public void Foo()
{
    Bar();
}

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

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

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

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

Ответ 10

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

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

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

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

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

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

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

В С# вы можете достичь той же цели с помощью метода расширения:

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

И оба они работают так же, как это здорово. Многие будут спорить о С++ отсутствующих методах расширения, многие будут спорить о С# отсутствующих функциях пространства имен.

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

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

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

Ответ 11

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

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