Недостатки конструкции C # (.NET) [закрыто]

84

Каковы самые большие недостатки дизайна в C # или .NET Framework в целом?

Пример: не существует строкового типа, не допускающего значения NULL, и вам нужно проверить DBNull при выборке значений из IDataReader.

Раухоц
источник
В каком смысле эти недостатки дизайна?
Джульетта,
С IDataReader вы можете использовать IsDBNull вместо проверки вручную
Марк Гравелл
9
Дайте понять Джону Скиту
3
Исправить IDataReader с помощью метода расширения довольно просто: см. Weblogs.asp.net/skillet/archive/2008/06/18/… .
Роберт Россни
@lagerdalek - Я бы добавил +1 к этому комментарию, если бы мог; хорошо помнят
Марк Грейвелл

Ответы:

39

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

В верхней части приведенного выше списка я бы также добавил следующие разумные запросы:

  1. ссылочные типы, не допускающие значения NULL, как дополнение к типам значений, допускающим значение NULL,
  2. разрешить переопределение пустого конструктора структуры,
  3. разрешить ограничениям универсального типа указывать запечатанные классы,
  4. Я согласен с другим автором, который запрашивал произвольные подписи конструктора при использовании в качестве ограничений, т.е. гдеT : new(string) или гдеT : new(string, int)
  5. Я также согласен с другим постером об исправлении событий, как для пустых списков событий, так и для параллельных настроек (хотя последнее сложно),
  6. операторы должны быть определены как методы расширения, а не как статические методы класса (или, по крайней мере, не как статические методы),
  7. разрешить статические свойства и методы для интерфейсов (в Java это есть, а в C # нет),
  8. разрешить инициализацию событий в инициализаторах объектов (в настоящее время разрешены только поля и свойства),
  9. почему синтаксис «инициализатор объекта» можно использовать только при создании объекта? Почему бы не сделать его доступным в любое время, т.е.var e = new Foo(); e { Bar = baz };
  10. исправить квадратичное перечислимое поведение ,
  11. все коллекции должны иметь неизменяемые моментальные снимки для итерации (т.е. изменение коллекции не должно приводить к недействительности итератора),
  12. кортежи легко добавлять, но эффективный замкнутый алгебраический тип, например "Either<T> " - нет, поэтому мне бы хотелось каким-нибудь способом объявить закрытый алгебраический тип и обеспечить для него исчерпывающее сопоставление с образцом (в основном первоклассная поддержка шаблона посетителя, но намного эффективнее); так что просто возьмите перечисления, расширьте их исчерпывающей поддержкой сопоставления с образцом и не допускайте недопустимых случаев,
  13. Мне бы очень хотелось поддержки сопоставления с образцом в целом, но, по крайней мере, для тестирования типов объектов; Мне также нравится синтаксис переключателя, предложенный в другом посте здесь,
  14. Я согласен с другим сообщением о том, что System.IOклассы вроде бы Streamплохо спроектированы; любой интерфейс, требующий некоторых реализаций, NotSupportedExceptionявляется плохим дизайном,
  15. IListдолжно быть намного проще, чем есть; фактически, это может быть верно для многих конкретных интерфейсов коллекций, например ICollection,
  16. слишком много методов генерируют исключения, например, IDictionary,
  17. Я бы предпочел более качественную форму проверенных исключений, чем та, которая доступна в Java (см. Исследование систем типов и эффектов, чтобы узнать, как это можно сделать),
  18. исправить различные раздражающие угловые случаи в разрешении перегрузки универсального метода; например, попробуйте предоставить два перегруженных метода расширения, один из которых работает со ссылочными типами, а другой - с типами структуры, допускающими значение NULL, и посмотрите, как это нравится вашему выводу типа,
  19. предоставить способ безопасного отражения имен полей и членов для интерфейсов, например INotifyPropertyChanged, которые принимают имя поля в виде строки; вы можете сделать это, используя метод расширения, который принимает лямбду с a MemberExpression, т.е. () => Foo, но это не очень эффективно,
    • Обновление: в C # 6.0 добавлен nameof()оператор для имен с одним членом, но он не работает в универсальных шаблонах ( nameof(T) == "T"вместо фактического имени аргумента типа: вам все равно нужно это сделать typeof(T).Name)) - и при этом он не позволяет вам получить строку «путь» , например, nameof(this.ComplexProperty.Value) == "Value"ограничение его возможных приложений.
  20. разрешить операторы в интерфейсах и реализовать все основные числовые типы IArithmetic ; также возможны другие полезные общие интерфейсы оператора,
  21. усложнить изменение полей / свойств объекта или, по крайней мере, разрешить аннотирование неизменяемых полей и заставить средство проверки типов применять его (просто относитесь к нему как к свойству только для получения данных, это несложно!); на самом деле объединять поля и свойства более разумным способом, поскольку нет смысла иметь и то, и другое; Автоматические свойства C # 3.0 - первый шаг в этом направлении, но они недостаточны,
    • Обновление: хотя в C # было readonlyключевое слово, а в C # 6.0 добавлены автоматические свойства только для чтения, хотя это не так строго, как настоящая языковая поддержка неизменяемых типов и значений.
  22. упростить объявление конструкторов; Мне нравится подход F #, но другой пост здесь, который требует просто "новый" вместо имени класса, по крайней мере, лучше,

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

Теперь по одной необоснованной просьбе:

  1. было бы очень-очень хорошо, если бы C # / CLR мог поддерживать полиморфизм конструкторов типов, т.е. дженерики над дженериками,

Довольно, пожалуйста? :-)

запрос
источник
1
Что касается №1, каждый тип должен либо иметь какое-то значение по умолчанию, либо система должна предоставлять средства для запуска конструктора всякий раз, когда выделяется переменная или поле определенного типа. Я бы предпочел последнее (ответвление # 2), но # 1 можно было бы разместить, если бы невиртуальные методы / свойства можно было бы декорировать, чтобы указать, что они должны вызываться без нулевой проверки. Это позволило бы таким вещам, как «String» поля вести себя так, как будто они по умолчанию имеют пустую строку, а не null (поскольку функция статической «длины» String может возвращать 0, если вызывается для пустой строки).
supercat
1
Что касается пункта 2, в целом было бы полезно, если бы структуры могли указывать не только конструктор, отличный от fill-with-zero, но и конструктор копирования, отличный от побайтного копирования. На самом деле, мне бы очень хотелось увидеть .net-подобный фреймворк, который мог бы хорошо распознавать, что сущности могут иметь семантику значений или ссылок, и позволять помечать объекты кучи значений как изменяемые, совместно используемые-неизменяемые или незафиксированные. (незафиксированный объект значения может быть видоизменен, если его режим сначала CompareExchange'd на изменяемый, или может быть общим, если его режим сначала CompareExchange'd на общий).
supercat
1
Отличные баллы! Для №1 аннотация системы типов является общим решением, но я предпочитаю распространять ограничения конструктора через переменные типа, такие как T: new (). Re: № 2, хорошее замечание о конструкторах копирования, но я был бы доволен более общими конструкторами, подобными тем, которые я описал выше. Еще лучше было бы полностью исключить конструкторы как выделенные методы и просто сделать их статическими методами. Это позволяет использовать более простые и общие шаблоны построения, особенно если мы разрешаем статические методы для интерфейсов. Конструкторы как статические методы + статические методы в интерфейсах тоже решают № 1.
naasking
3
# 3: В чем смысл использования запечатанного класса в качестве параметра универсального типа, например Foo <T>, где T: string? №11: Хорошо, у меня List<T>миллион ТС. Как вы предлагаете сделать снимок эффективно? №21: Используйте readonlyключевое слово .... хотя здесь есть несколько хороших предложений, в основном они всего лишь предложения, а не недостатки дизайна.
Qwertie 03
2
Это очень интересный ответ, но я думаю, что нам следует обновить его функциями C # 6. Пример: пункты 19 и 21 были реализованы =)
eduardobr
72
  • Reset()метод на IEnumerator<T>была ошибка (для блоков итераторов, язык SPEC даже требования , что это бросает исключение)
  • методы отражения, возвращающие массивы, по мнению Эрика, ошибкой
  • ковариация массивов была и остается диковинкой
    • Обновление: В C # 4.0 с .NET 4.0 добавлена ​​поддержка ковариантности / контравариантности для общих интерфейсов (например, IEnumerable<out T>и Func<in T, out TResult>, но не конкретных типов (например List<T>).
  • ApplicationException скорее впал в немилость - это было ошибкой?
  • синхронизированные коллекции - хорошая идея, но не обязательно полезная в реальности: обычно вам нужно синхронизировать несколько операций ( Contains, тогдаAdd ), поэтому коллекция, которая синхронизирует отдельные операции, не так уж и полезна
    • Обновление: В System.Collections.Concurrentтипы , с TryAdd, GetOrAdd, TryRemoveи т.д. были добавлены в .NET Framework 4.0 - хотя методы , которые принимают заводскую делегат не гарантируют завод будет запущен только один раз за ключ.
  • можно было бы больше использовать шаблон using/ lock- возможно, позволяя им совместно использовать повторно используемый (расширяемый?) синтаксис; вы можете смоделировать это, вернувшись IDisposableи используя using, но это могло быть более понятным
  • блоки итераторов: нет простого способа проверки аргументов заблаговременно (а не лениво). Конечно, вы можете написать два связанных метода, но это некрасиво
  • было бы неплохо более простая неизменяемость; C # 4.0 немного помогает , но недостаточно
  • нет поддержки "этот параметр типа ссылки не может быть нулевым" - хотя контракты (в 4.0) в некоторой степени помогают в этом. Но синтаксис вроде Foo(SqlConnection! connection)(который вводит нулевую проверку / throw) был бы хорош (в отличие отint? т. Д.)
  • отсутствие поддержки операторов и нестандартных конструкторов с дженериками; С # 4.0 немного решает эту проблему с помощью dynamic, или вы можете включить его так
  • переменная итератора объявляется за пределами while вforeach расширении, что означает, что анон-методы / лямбда-выражения захватывают единственную переменную, а не одну на итерацию (болезненно с потоковой / асинхронной / и т. д.)
Марк Гравелл
источник
IEnumerable! = IEnumerable <object> действительно нечетный
Rauhotz 04
2
Что ж, IEnumerable - это пережиток версии 1.1; вы можете использовать .Cast <object> () с LINQ, по крайней мере
Марк Грейвелл
8
Ребята из BCL сказали, что это ApplicationExceptionбыла ошибка - не полезная, как они надеялись. Они также сказали, что так System.Exceptionдолжно было быть abstract.
Jay Bazuzi 05
2
Не допускающий значения NULL: передача обычного ссылочного типа T в объект, который принимает не допускающий значение NULL T, должна вызывать ошибку компиляции! (точно так же, как вы не можете передать int? в int). Конечно, можно перейти в другую сторону.
Jay Bazuzi 05
1
@Jon Harrop: IMHO, должна была быть поддержка неизменяемых массивов и ссылок на массив только для чтения. Возможно, и для некоторых других вариантов массива (например, «массив с изменяемым размером» (косвенная ссылка) или ссылка на массив со смещением и границей).
supercat
60

TextWriter - это базовый класс StreamWriter. что за?

Это всегда меня до крайности смущает.

Приятно
источник
19
+1 Мне каждый раз приходится это искать. (Что значит, что я не могу использовать новый TextWriter ()?)
Николас Пясецки,
1
Слава богу ... Я думал, что это только я.
IJ Kennedy
? Всегда что-то записывает текст, но только StreamWriter делает это с потоком. Кажется довольно простым.
Джон Ханна,
4
Имя StreamWriter не делает тот факт, что он пишет текст, достаточно очевидным IMO. Это звучит изолированно, как если бы он просто записывал байты, а TextWriter был бы причудливой реализацией поверх, которая преобразовывала бы api (string toWrite) в байты для вас. Теперь, если бы он назывался StreamTextWriter, тогда, конечно, это было бы сразу очевидно, но немного длинно :(
Quibblesome
44

Маленький любимец C # - конструкторы используют синтаксис C ++ / Java, в котором имя конструктора совпадает с именем класса.

New() или же ctor() было бы намного лучше.

И конечно, такие инструменты, как coderush, делают эту проблему менее сложной для переименования классов, но с точки зрения удобочитаемости New () обеспечивает большую ясность.

Скотт Вайнштейн
источник
Эмм, как он узнает, что вы пытаетесь создать новый экземпляр?
BlueRaja - Дэнни Пфлугхёфт
4
@BlueRaja: Скотт имеет в виду именование конструкторов в классах. class Foo { new(int j) {i = j} int i; }
далле
Хотя я на 100% согласен с тем, что ctor () или constructor () были бы лучше ( Newключевые слова --uppercase не противоречат соглашению), я не решаюсь назвать это недостатком дизайна. Они хотели привлечь существующих разработчиков C ++ / Java, и заимствование множества глупых старых синтаксических соглашений, возможно, помогло им достичь своей цели.
Qwertie 03
связанный с этим вопросом: stackoverflow.com/questions/32101993/c-sharp-sorted-linkedlist нет способа (с использованием только .NET) просто отсортировать LinkedList с помощью MergeSort, bucketsort или любого другого алгоритма сортировки, но с использованием linq, который намного медленнее чем специальная реализация.
CoffeDeveloper
29

Я не понимаю, что ты не можешь

где T: new (U)

Таким образом, вы объявляете, что универсальный тип T имеет конструктор, отличный от конструктора по умолчанию.

редактировать:

Я хочу сделать это:

public class A 
{
    public A(string text) 
    {

    }
}


public class Gen<T> where T : new(string text) 
{

}
оборота туинстоэль
источник
Универсальный тип объявляет, как вы будете использовать объекты этого типа, то есть их интерфейс. Конструктор - это деталь реализации этого интерфейса, которая не является заботой потребителя. Когда вам нужно создать параметризованный экземпляр, используйте фабрику.
Брайан Уоттс
Для информации, хотя он не может выполнять проверку во время компиляции, в MiscUtil есть некоторый код для эффективного использования конструкторов, отличных от используемых по умолчанию (в дженериках), то есть без Activator.CreateInstance или отражения.
Марк Грейвелл
Потому что это не имеет смысла и в некоторых случаях сбивает с толку. Тем не менее это может быть полезно при работе с неизменяемыми объектами.
Pop Catalin
7
В целом, да, отсутствие ограничений для участников раздражает.
MichaelGG
3
Аминь, я всегда хотел этого
Стив
20

Я очень удивлен, что первым упомянул об этом:

Типизированные наборы данных ADO.NET не раскрывают столбцы, допускающие значение NULL, как свойства типов, допускающих значение NULL. Вы должны уметь написать это:

int? i = myRec.Field;
myRec.Field = null;

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

int? i = (int?)myRec.IsFieldNull() ? (int?)null : myRec.Field;
myRec.SetFieldNull();

Это раздражало в .NET 2.0, и еще больше раздражает то, что вам приходится использовать jiggery-pokery, как указано выше, в ваших красивых аккуратных запросах LINQ.

Также раздражает то, что сгенерированный Add<TableName>Rowметод также нечувствителен к понятию обнуляемых типов. Тем более что сгенерированныеTableAdapter методов нет.

В .NET не так много всего, что заставляет меня чувствовать, что команда разработчиков сказала: «Хорошо, ребята, мы достаточно близки - отправьте его!» Но это точно так.

Роберт Россни
источник
Я полностью согласен! Это меня беспокоит каждый раз, когда мне приходится его использовать (что бывает часто). Ах! +1
Эйвинд
2
Самое меньшее, что они могли сделать, - это придумать новый класс DataSetV2 (плохое имя - просто ради аргумента), который использовал бы типы значений, допускающие значение NULL, вместо DBNull повсюду.
Кристиан Хейтер,
Давайте не будем забывать абсурдность требования специального значения DBNull.Value, когда nullсамо по себе было бы вполне достаточно для представления NULL. К счастью, LINQ-to-SQL просто использует null вместо NULL.
Qwertie 03
На самом деле этот абсурд и есть камень, на котором построено все абсурдное здание.
Роберт Россни 05
20
  1. Я не большой поклонник классов Stream, StringWriter, StringReader, TextReader, TextWriter ... просто не понятно, что к чему.
  2. IEnumerable.Reset генерирует исключение для итераторов. У меня есть некоторые сторонние компоненты, которые всегда вызывают сброс при связывании данных, и для их использования требуется сначала привести к списку.
  3. Сериализатор Xml должен иметь сериализованные элементы IDictionary
  4. Я совершенно забыл о HttpWebRequest и FTP API, какая боль у меня .... (спасибо за комментарий Николас, чтобы напомнить мне об этом :-)

редактировать
5. Еще одно мое раздражение - это то, что System.Reflection.BindingFlags может использоваться по-разному в зависимости от используемого вами метода. Например, в FindFields что означает CreateInstance или SetField? Это тот случай, когда они перегрузили смысл этого перечисления, что сбивает с толку.

оборота Джош Берке
источник
1
+1 Мне каждый раз приходится искать любой из классов XmlTextWriter, TextWriter и т. Д. То же самое с материалом HttpWebRequest / Response. Совершенно неинтуитивный API.
Николас Пясецкий
+ 1-1 = 0: XmlTextWriter и т. Д., Я согласен, что невозможно точно вывести, что они из названия. HttpWebRequest Я не согласен, мне это кажется довольно интуитивным.
AnthonyWJones,
Полагаю, каждому свое. Думаю, с FTP я ожидал бы более высокого уровня абстракции, чем то, что есть у них.
JoshBerke
15

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

VB:

Dim a = Function(x) x * (x - 1)

C #

Было бы неплохо, если бы можно было сделать так:

var a = x => x * (x - 1);

Вместо того, чтобы делать это:

Func<int, int> a = x => x * (x - 1);

Я понимаю, что это не намного дольше, но в Code Golf каждый персонаж чертовски важен! Разве они не принимают это во внимание при разработке этих языков программирования? :)

БенАлабастер
источник
3
Microsoft должна учитывать Code Golf при разработке языков?
jrcs3 05
3
@ Рэй Бернс: Как это узнать в VB? VB поддерживает это, так в чем разница?
BenAlabaster
3
@RayBurns Вывод типа? Я использую это с 1989 года.
RD1
3
Ламды гомоиконны в C #. фрагмент (int x) => x * (x -1);может означать Func<int, int>или может означатьExpression<Func<int, int>>
Скотт Вайнштейн
3
@BenAlabaster: VB поддерживает арифметические операторы с поздним связыванием. C # должен разрешить их во время компиляции. Это разница в языке. Например, VB может складывать два объекта вместе. C # не может, потому что +не определен для object.
рекурсивный
14
  1. Класс System.Object :

    • Equals и GetHashCode - не все классы сопоставимы или хешируемы, следует перенести в интерфейс. На ум приходит IEquatable или IComparable (или аналогичный).

    • ToString - не все классы можно преобразовать в строку, их следует переместить в интерфейс. На ум приходит IFormattable (или аналогичный).

  2. Свойство ICollection.SyncRoot :

    • Способствует плохому дизайну, внешний замок почти всегда полезнее.
  3. Дженерики должны были быть там с самого начала:

    • Пространство имен System.Collections содержит множество более или менее устаревших классов и интерфейсов.
Далле
источник
1
1. Эти методы настолько распространены, что было решено, что со странностями все в порядке, имеет ли значение, может ли кто-то вызвать Object.Equals в вашем классе? Известно, что реализация может быть или не быть, и требование: IEquatable, IFormattable для 99% классов является нечетным.
Guvante
1
Полезно иметь возможность, например, создать словарь с определяемыми пользователем объектами в качестве ключей, используя равенство ссылок по умолчанию, без необходимости явно добавлять код к определяемым пользователем объектам для этой цели. Я бы счел Finalize гораздо более пустой тратой (лучшей альтернативой было бы иметь объекты, которые потребуют финализации, реализовывать iFinalizable и явно регистрировать себя для завершения). OTOH, должно было быть больше встроенной поддержки iDisposable, включая вызов Dispose, если конструктор выдает исключение.
supercat
1
@supercat: Все, что нужно, - это EqualityComparer<T>.Defaultправильно обновить . Тогда оба var dict = new Dictionary<object, string>(EqualityComparer<object>.Default)и var dict = new Dictionary<object, string>()будут использовать сравнение / равенство ссылок.
далле
1
@supercat: То, что вы описываете, именно то, что EqualityComparer<T>.Defaultделает. Нет необходимости проверять каждый поиск. Компаратор - это свойство Dictionaryэкземпляра, и каждый Dictionaryзнает, какое из них он использует.
далле
1
@supercat: словарь должен использовать наиболее общий тип (то есть общий базовый класс) в качестве ключа, использование как Strings, так и DateTime в одном словаре не будет иметь никакого смысла, если не используется сравнение ссылок, если пользовательское сравнение не доказано, что является. Помните, что название этой темы - «Недостатки дизайна C # (.NET)».
далле
12

Одна из вещей, которая меня раздражает, - это Predicate<T> != Func<T, bool>парадокс. Они оба являются делегатами типа, T -> boolно не совместимы по присваиванию.

Грег Бич
источник
Есть трюк с использованием Delegate.Create и некоторого приведения для преобразования, но, по крайней мере, было бы неплохо иметь возможность выполнить явное приведение (однако я могу понять отсутствие поддержки неявного)
Guvante
Дизайн делегатов в целом ошибочен; например, отсутствие слабых событий (реализация слабых событий на стороне источника без особых усилий со стороны подписчика может быть выполнена только с помощью набора отражений и ReflectionPermission, см. codeproject.com/Articles/29922/Weak-Events-in-C ), и неэффективность, которая проистекает из требования, что делегаты должны быть ссылочными типами (делегаты были бы быстрее и во многих случаях использовали бы 1/3 объема памяти, если бы они были типами значений - тогда они были бы просто парой указатели, которые можно передать в стек.)
Qwertie
11

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

ChrisW
источник
Вы должны иметь возможность NGEN для вашей программы перед запуском.
Otávio Décio 04
Не то же самое, что удаление зависимостей среды выполнения. Вы сохраняете только первый запуск JIT вашего кода.
Эд С.
Разве это не средство запутывания? Он встроит фреймворк в ваш exe, поэтому вам не нужно его развертывать. Я не могу вспомнить его название ... это не PreEmptive ...
Джош Берке
2
Разве Postbuild Xenocode не делает этого? Было бы неплохо, если бы у Visual Studio был способ сделать это ...
БенАлабастер, 05
11

Мы так много знаем о праве методах объектно-ориентированного программирования. Разделение, программирование по контракту, избежание неправильного наследования, правильное использование исключений, открытый / закрытый принципал, заменяемость Лискова и т. Д. Как бы то ни было, в .Net-фреймворках не используются лучшие практики.

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

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

Дэниел Полл
источник
4
+1 за нападки на цель; Я бы добавил к этому невозможность создания подклассов для каждого класса, отсутствие интерфейсов для фундаментальных классов и нежелание исправлять ошибки фреймворка даже по прошествии нескольких лет
Стивен А. Лоу
1
Если мы перейдем к делу, говорим ли мы, что фреймворки .Net настолько плохи, что трудно решить, какой из недостатков является наихудшим? Я чувствую себя лучше после выступления и ценю положительный отзыв, так как ожидал, что меня кричат ​​фанаты MS.
Дэниел Полл
Я не голосовал в любом случае, и в любом случае я очень не хочу отдавать голоса против, но я пытаюсь понять, почему мне ПРОСТО НЕ ЗАБОТАТЬ.
Майк Данлэйви, 05
2
Я думаю, вы говорите: «Это не так хорошо, как могло бы быть», но это не ответ. Ничто не идеально. Укажите конкретику.
jcollum 05
3
Нет, я говорю, что есть множество случаев, когда дизайн явно ошибочен, но когда вы думаете, что они все делают правильно, они все равно ошибаются. Например, мой пост на форумах MSDN здесь: social.msdn.microsoft.com/forums/en-US/wpf/thread/…
Дэниел Полл
11

Мне не нравится оператор switch в C #.

Я бы хотел что-то подобное

switch (a) {
  1    : do_something;
  2    : do_something_else;
  3,4  : do_something_different;
  else : do_something_weird; 
}

Так что больше никаких разрывов (легко забыть) и возможности разделять запятыми разные значения.

Tuinstoel
источник
На самом деле, я думаю, было бы даже лучше, если бы для этого потребовался либо один оператор, либо блок в фигурных скобках - как и все остальное в C #. Без возможности провалиться текущий синтаксис прерывания будет немного хитрым и не ограничивает область видимости. (AFAIK, возможно, я не знаю)
Тамаш Чинеге
Я не понимаю о чем ты. Опубликуйте здесь свой собственный «идеальный» оператор переключения. Я не хочу проваливаться, но значения, разделенные запятыми.
tuinstoel
8
Кстати, я согласен с ОП. switchфундаментально нарушен на всех языках, которые эмулируют намеренно урезанную версию C (оптимизированную для скорости!). VB работает намного лучше, но все еще на световые годы отстает от языков с сопоставлением с образцом (Haskell, F #…).
Конрад Рудольф
1
tuinostel: что-то вроде switch (a) {case 1 {do_something; } case 2 {do_something_else; }} - то есть избавление от оператора break и требование правильных блоков кода для каждого случая
Тамас Чинеге
2
В общем, наличие break кажется ошибкой, разве это не ошибка компиляции? Похоже, это только для облегчения перехода с C на C # (также известного как обучение разработчиков тому, что они не могут автоматически провалиться)
Guvante
10

События в C #, где вы должны явно проверять слушателей. Разве не в этом был смысл событий транслировать для всех, кто там окажется? Даже если их нет?

Томас Эйд
источник
1
Раздражает, что для этого нет сахара, когда вы этого хотите, я понимаю, почему они делают это жестко, это поощряет не создавать экземпляры аргументов события, если они не нужны
ShuggyCoUk
Хм. Я либо не понимаю, либо не согласен, либо и то, и другое :-). Я не могу сказать, что когда-либо чувствовал разочарование при создании чего-либо. Для меня это пахнет преждевременной оптимизацией.
Thomas Eyde
В некоторых случаях частичные методы являются жизнеспособной альтернативой.
Роберт Харви,
ПЛЮС все связи, которые это вызывает ... MS имеет CAB, который исправляет обе эти проблемы, но у CAB есть много собственных проблем из-за ограничений в C # (например, строки, а не перечисления в качестве тем событий) - почему бы просто не сделать свободно -связанные события частью языка !?
BlueRaja - Дэнни Пфлугхёфт
9

Ужасное (и совершенно незаметное для большинства) поведение вложенных / рекурсивных итераторов O (N ^ 2) .

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

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

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

ShuggyCoUk
источник
Это особенно ошибочно, потому что оно противоречит интуитивным ожиданиям от поведения O и будет заманить в ловушку многих людей
oefe
7

Некоторые классы реализуют интерфейсы, но они не реализуют многие методы этого интерфейса, например, Array реализует IList, но 4 из 9 методов выдают NotSupportedException http://msdn.microsoft.com/en-us/library/system.array_members .aspx

оборота ggf31416
источник
Ну, вы не можете изменить количество элементов в массиве, поэтому нет ничего, что могли бы сделать Add, Clear, Insert и Remove (At), кроме как выбросить NotSupported ... Фактически, я ожидал ЛЮБОЙ реализации IList, которая возвращает true для IsFixedSize бросит на них.
CB
@CB, я думаю, немного опоздал на вечеринку :) Но если Array не может удовлетворить "IList", зачем вообще его реализовывать? Это нарушение принципа L в SOLID.
Вингер
7

Статические члены и вложенные типы в интерфейсах.

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

Джей Базузи
источник
1
Разве это не похоже на другое ваше предложение?
RCIX
1
Нет, это о языке C #, а другое о фреймворке. Не всех заботит различие, поэтому позвольте мне сказать, что одно касается того, что разрешено, а другое - того, что предоставляется.
Jay Bazuzi,
6

Ужасно опасный дефолтный характер событий. Тот факт, что вы можете вызвать событие и оказаться в несогласованном состоянии из-за удаления подписчиков, просто ужасен. См. Отличные статьи Джона Скита и Эрика Липперта для получения дополнительной информации по этой теме.

Джейсон
источник
Я бы не стал возражать, если бы события по умолчанию не были потокобезопасными (это могло бы повысить производительность в однопоточном коде); что глупо, так это то, что добавление / удаление безопасно по умолчанию, но естественный способ инициировать событие небезопасен, и нет возможности легко сделать его безопасным.
Qwertie 03
@Qwertie: Что еще глупее, так это то, что некоторое время при добавлении / удалении использовалась блокировка, но все же не было поточно-ориентированным.
supercat 06
6
  • null везде.

  • const нигде.

  • API несовместимы, например, изменение массива возвращает void но добавление к нему StringBufferвозвращает тот же изменяемый StringBuffer.

  • Интерфейсы коллекций несовместимы с неизменяемыми структурами данных, например Add , в System.Collections.Generic.IList<_>не может возвращать результат.

  • Нет структурной типизации, поэтому вы пишете System.Windows.Media.Effects.SamplingMode.Bilinear вместо просто Bilinear.

  • Мутабельный IEnumerator интерфейс, реализуемый классами, когда он должен быть неизменяемым struct.

  • Равенство и сравнение беспорядок: у вас есть , System.IComparableи Equalsно тогда вы также получили System.IComparable<_>, System.IEquatable,System.Collections.IComparer , System.Collections.IStructuralComparable, System.Collections.IStructuralEquatable, System.Collections.Generic.IComparerи System.Collections.Generic.IEqualityComparer.

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

JD
источник
В CLR нет структурной типизации, но вы, кажется, смешали структурную типизацию с выводом типа или с функцией Ruby, известной как «символы». Структурная типизация была бы, если бы среда CLR считала Func <int, bool> и Predicate <int> одним и тем же типом или, по крайней мере, неявно конвертируемыми.
Qwertie 03
Говоря о сравнении, не забывайте Comparer <T>!
Qwertie 03
@Qwertie Я имел в виду такие особенности языка программирования, как полиморфные варианты в OCaml. В библиотеке OCaml LablGL есть много интересных примеров использования структурной типизации в контексте графики. Ничего общего с выводом типа и только косвенно связано с символами.
JD
1
Как использовать неизменяемую структуру IEnumerator?
supercat 06
5

0 подрабатывает enum

особенности enum: http://blogs.msdn.com/abhinaba/archive/2007/01/09/more-peculiarites-of-enum.aspx

как показано на этом хорошем примере: http://plus.kaist.ac.kr/~shoh/postgresql/Npgsql/apidocs/Npgsql.NpgsqlParameterCollection.Add_overload_3.html

мое предложение, используйте знак «@» с пользой:

вместо:

если ((myVar & MyEnumName.ColorRed)! = 0)

использовать этот:

если ((myVar & MyEnumName.ColorRed)! = @ 0)

Майкл Буэн
источник
1
+1 enum - одна из немногих вещей, которые Java сделала правильно, а C #
BlueRaja - Danny Pflughoeft
5

Чтобы добавить к длинному списку хороших моментов, уже отмеченных другими:

  • DateTime.Now == DateTime.Now в большинстве, но не во всех случаях.

  • Stringкоторый является неизменным, имеет множество опций для построения и манипуляции, но StringBuilder(который является изменяемым) - нет.

  • Monitor.Enterи Monitor.Exitдолжны были быть методами экземпляра, поэтому вместо создания определенного объекта для блокировки вы можете создать новый a Monitorи заблокировать его.

  • Деструкторы никогда не должны были называться деструкторами. Спецификация ECMA называет их финализаторами, что гораздо меньше сбивает с толку C ++, но спецификация языка по-прежнему называет их деструкторами.

Брайан Расмуссен
источник
3
DateTime.NowОдин в мире наиболее очевидная гонка условие, но +1 для остального
BlueRaja - Дэнни Pflughoeft
Дело не в том, что это состояние гонки, а в том, что они сделали его собственностью. Свойства выглядят в точности как поля, поэтому это довольно удивительное поведение IMO.
Брайан Расмуссен,
4
@Brian Rasmussen: DateTime.Now вполне правильно является свойством, поскольку оно изменяется не при чтении, а под воздействием внешних факторов. Если кто-то читает такое свойство, как SomeForm.Width, а затем - после того, как пользователь изменил размер формы - один читает его снова, значение при втором чтении будет другим. Хотя возможно, что выполнение первого DateTime.Now может занять достаточно много времени, чтобы повлиять на значение, считываемое вторым, такой эффект не будет отличаться от любой другой функции, выполнение которой заняло такое же время.
supercat
4

Иногда меня раздражает то, как мы используем свойства. Мне нравится думать о них как о эквиваленте методов Java getFoo () и setFoo (). Но это не так.

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

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


private string password;

public string Password
{
    // Called when being set by a deserializer or a persistence
    // framework
    deserialize
    {
       // I could put some backward-compat hacks in here. Like
       // weak passwords are grandfathered in without blowing up
       this.password = value;
    }
    get
    {
       if (Thread.CurrentPrincipal.IsInRole("Administrator"))
       {
           return this.password;
       }
       else
       {
           throw new PermissionException();
       }
    }
    set
    {
       if (MeetsPasswordRequirements(value))
       {
           throw new BlahException();
       }
       this.password = value;
    }
    serialize
    {
        return this.password;
    }
}

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

Николай Пясецкий
источник
3
Я считаю, что они предоставляют интерфейс ISerializable и неявный конструктор для подобных вещей, например, когда вы не хотите, чтобы сериализатор просто вызывал свойства. Это немного больше работы, но похоже, что вы уже проделали большую часть этой работы с помощью своего метода.
Guvante
4

Методы расширения хороши, но это уродливый способ решить проблемы, которые можно было бы решить более чисто с помощью настоящих миксинов (посмотрите на Ruby, чтобы понять, о чем я говорю) в отношении миксинов. Действительно хороший способ добавить их в язык - разрешить использование дженериков для наследования. Это позволяет вам расширять существующие классы приятным объектно-ориентированным способом:

public class MyMixin<T> : T
{
    // etc...
}

это можно использовать, например, для расширения строки:

var newMixin = new MyMixin<string>();

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

Извините за тираду :-)

Мендельт
источник
5
Интересно, но я предпочитаю методы расширения. Если я получаю библиотеку, содержащую кучу методов расширения для строк, я не хочу, чтобы мне приходилось менять все строковые ссылки на MyMixin <string>, чтобы получить новый материал. Конечно, это мелочь, но именно прозрачное добавление методов делает методы расширения такими хорошими.
RCIX
Кстати, знаете ли вы, что это уже работает?
RCIX
2
Я не понимаю, как LINQ может работать таким образом
BlueRaja - Дэнни Пфлугофт
2
@RCIX: Миксины звучат так, как я думал, должны работать методы расширения. Проблема с созданием неявных методов расширения заключается в том, что это означает, что реальные члены класса должны иметь приоритет над методами расширения. Если определен метод расширения Graphics.DrawParallelogram (Pen p, Point v1, Point v2, Point v3), а затем в System.Graphics добавлена ​​функция DrawParallelogram, которая использует точки в другом порядке, код, использующий метод расширения, сломается без предупреждение. Кстати, были бы проблемы с использованием двух точек для методов расширения (например, object..method ()?)
supercat
3

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

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

Джошуа
источник
1
Какие очевидные ошибки фреймворка вы имеете в виду?
Роберт Россни 05
1
# 1 Щелкните частично видимый дочерний элемент управления прокручиваемого элемента управления. Элемент управления перемещается в поле зрения до того, как он получит событие MouseDown, в результате чего щелчок будет в другом месте элемента управления, чем ожидалось. Хуже в древовидных представлениях, где также запускается операция перетаскивания.
Джошуа
1
# 2 это # 3 это # 4 это
BlueRaja - Дэнни Пфлугхофт
3
  • Возможность вызвать метод расширения для нулевой переменной спорна, например

    объект a = null; a.MyExtMethod (); // это вызывается, предположим, что где-то он определил MyExtMethod

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

  • Один недостаток в названии. Буква «C» в слове «конфигурация» в System.configuration.dll должна начинаться с заглавной буквы.

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

оборота codemeit
источник
3
Тем не менее, очень удобно - у меня есть метод расширения ThrowIfNull для проверки параметров ;-p
Marc Gravell
2
ты можешь это сделать? тьфу ThrowIfNull, это интересное расширение, но это кажется неправильным.
JoshBerke 05
1
В простой среде CLR вы можете вызывать методы экземпляра для нулевых ссылок, и если метод не обращается к объекту или его полям, тогда вызов не вызывает исключение нулевой ссылки. (Вы не можете сделать это на C #, поскольку он использует callvirt даже для не виртуальных методов)
Pop Catalin
7
исключение совершенно неверно. Вы не можете быстро потерпеть неудачу, если ВЫ ДОЛЖНЫ перехватить каждое чертово исключение, которое может быть выброшено в стек вызовов. Но я ДЕЙСТВИТЕЛЬНО хочу, чтобы было намного проще идентифицировать любые исключения, которые могут быть созданы в конкретном вызове и его результирующем стеке вызовов ...
3
@Will: Обработка исключений неприятна как в Java, так и в .net, поскольку используемый механизм тесно связывает вместе три концепции, которые в некоторой степени связаны, но также несколько ортогональны: (1) Какие типы вещей пошли не так (ошибка границ массива, тайм-аут ввода-вывода и т. д.); (2) Должен ли определенный код действовать в результате; (3) В какой момент проблема должна считаться «решенной». Рассмотрим процедуру, которая должна изменять объект данными, считываемыми из IEnumerable. Что должно произойти, если при обработке этого IEnumerable возникнет исключение?
supercat
3

Метод .Parameters.Add () в SqlCommand в V1 фреймворка был ужасно спроектирован - одна из перегрузок в основном не работала бы, если бы вы передали параметр со значением (int) 0 - это привело к их созданию метод .Parameters.AddWithValue () в классе SqlCommand.

Дэйв Маркл
источник
Я согласен, но я думаю, вы имеете в виду метод SqlCommand.Parameters.Add ().
Мэтт Петерсон,
3
  1. Нет подмножеств ICollection<T>и IList<T>; как минимум, ковариантный интерфейс коллекции только для чтенияIListSource<out T> (с перечислителем, индексатором и счетчиком) был бы чрезвычайно полезен.
  2. .NET не поддерживает слабые делегаты . Обходные пути в лучшем случае неуклюжие, а обходные пути на стороне слушателя невозможны при частичном доверии (требуется ReflectionPermission).
  3. Универсальная унификация интерфейса запрещена, даже если она имеет смысл и не вызывает проблем.
  4. В отличие от C ++, ковариантные возвращаемые типы в .NET не допускаются.
  5. Побитовое сравнение двух типов значений на равенство невозможно. В функциональной « постоянной » структуре данных я писалTransform(Sequence<T>, Func<T,T>) функцию, которая должна была быстро определить, возвращает ли функция то же значение или другое значение. Если функция не изменяет большую часть / все свои аргументы, то выходная последовательность может совместно использовать часть / всю память из входной последовательности. Без возможности побитового сравнения любого типа значения T необходимо использовать гораздо более медленное сравнение, что сильно снижает производительность.
  6. .NET, похоже, не в состоянии поддерживать специальные интерфейсы (вроде тех, что предлагаются в Go или Rust) с высокой производительностью. Такие интерфейсы позволили бы вам выполнить приведение List<T>к гипотетическому IListSource<U>(где T: U), даже если класс явно не реализует этот интерфейс. Есть как минимум три разные библиотеки (написанные независимо) для обеспечения этой функциональности (конечно, с недостатками производительности - если бы идеальный обходной путь был возможен, было бы несправедливо называть это недостатком в .NET).
  7. Другие проблемы с производительностью: IEnumerator требует двух вызовов интерфейса на итерацию. Простые указатели на методы (открытые делегаты размера IntPtr) или делегаты с типом значений (IntPtr * 2) невозможны. Массивы фиксированного размера (произвольного типа T) не могут быть встроены в классы. Нет WeakReference<T>(вы можете легко написать свой собственный, но он будет использовать внутренние преобразования).
  8. Тот факт, что идентичные типы делегатов считаются несовместимыми (без неявного преобразования), в некоторых случаях меня раздражал (например, Predicate<T>vs Func<T,bool>). Мне часто хотелось бы иметь структурную типизацию для интерфейсов и делегатов, чтобы добиться более слабой связи между компонентами, потому что в .NET недостаточно, чтобы классы в независимых библиотеках DLL реализовали один и тот же интерфейс - они также должны иметь общую ссылку на третью DLL, определяющая интерфейс.
  9. DBNull.Valueсуществует, хотя nullи одинаково хорошо служил бы той же цели.
  10. В C # нет оператора ?? =; ты должен написать variable = variable ?? value. Действительно, в C # есть несколько мест, в которых без нужды отсутствует симметрия. Например, вы можете писать if (x) y(); else z();(без фигурных скобок), но не можете писать try y(); finally z();.
  11. При создании потока невозможно заставить дочерний поток наследовать локальные значения потока от родительского потока. BCL не только не поддерживает это, но вы не можете реализовать это самостоятельно, если не создадите все потоки вручную; даже если бы произошло событие создания потока, .NET не может сказать вам «родителей» или «потомков» данного потока.
  12. Тот факт, что для разных типов данных существуют два разных атрибута длины, «Длина» и «Счетчик», является незначительной неприятностью.
  13. Я мог бы бесконечно говорить о плохом дизайне WPF ... и WCF (хотя и весьма полезный для некоторых сценариев) также полон бородавок. В целом, раздутость, неинтуитивность и ограниченная документация многих новых подбиблиотек BCL заставляют меня неохотно их использовать. Многие из новых вещей могли бы быть намного проще, меньше, проще в использовании и понимании, более слабо связаны, лучше документированы, применимы к большему количеству вариантов использования, быстрее и / или более строго типизированы.
  14. Меня часто укусила ненужная связь между геттерами и сеттерами свойств: в производном классе или производном интерфейсе вы не можете просто добавить сеттер, когда базовый класс или базовый интерфейс имеет только геттер; если вы переопределите геттер, вам не разрешено определять сеттер; и вы не можете определить сеттер как виртуальный, а получатель как не виртуальный.
Qwertie
источник
Я согласен с вами насчет подмножеств IList<T>, хотя я бы использовал IReadableByIndex<out T>и IAppendable<in T>. Многие из ваших других вещей - вопли, с которыми я тоже согласен.
supercat 06
Это действительно длинное имя. Может быть, мы могли бы пойти на компромисс IListReader<T>;) - Я использую слово «источник» как антоним слова «сток» (интерфейс только для записи).
Qwertie
Может быть IListSource<in T>или IReadableList<out T>. Может быть полезно, чтобы базовые типы интерфейсов включали методы, которые существуют не во всех производных, хотя я думаю, что часто бывает хорошо иметь несколько специализированных интерфейсов. Например, один может IList<T>содержать методы изменения размера, которые могут работать или не работать, и IResizableList<T>который реализует те же методы, но гарантирует, что они должны работать. Такой подход может быть полезен в тех случаях, когда поле может содержать единственную существующую ссылку на изменяемый список или общую ссылку на неизменяемый.
supercat
В таком случае код, который хочет изменить содержимое списка, проверит, является ли он изменяемым типом, и, если нет, сгенерирует новый изменяемый экземпляр, содержащий те же элементы, что и неизменяемый список, а затем начнет его использовать. Было бы утомительно, если бы коду приходилось постоянно приводить тип поля каждый раз, когда он хотел бы использовать для него метод изменения.
supercat
@supercat Это только утомительно, потому что C # не предоставляет действительно простого способа проверить, реализован ли интерфейс, и немедленно его использовать. MS должна добавить языковую функцию, чтобы упростить задачу. Я предпочитаю использовать обязывающее выражение if (rl:(list as IResizableList<T>) != null) rl.Add(...);, но есть и другие предложения. Меня как автора различных коллекций и адаптеров коллекций раздражает написание множества фиктивных методов, которые генерируют исключения. Как поклонник типобезопасности, я не хочу, чтобы мне разрешали вызывать незаконные методы. Поклонник IntelliSense, я не хочу видеть их в списке.
Qwertie
2

Одна вещь , которая галочкой меня в 1.x было при использовании System.Xml.XmlValidatingReader, то ValidationEventHandler«s ValidationEventArgsне выставляет Базового XmlSchemaException(помечено внутренний) , который имеет всю полезную информацию , как linenumberи position. Вместо этого вы должны проанализировать это из свойства строки сообщения или использовать отражение, чтобы раскопать его. Не очень хорошо, если вы хотите вернуть конечному пользователю более исправленную ошибку.

Кев
источник
1

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

    enum Colors { white, blue, green, red, black, yellow }

    enum SpecialColors { Colors.blue, Colors.red, Colors.Yellow } 
Tuinstoel
источник
2
Хотя это даже не имеет смысла. typeof(Color)! = typeof(SpecialColors).
Кирк Уолл
10
Это достаточно просто сделать:enum SpecialColors { blue = Colors.blue, red = Colors.red, yellow = Colors.Yellow }
Trystan Spangler
0

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

Из MSDN:

  • var может использоваться только тогда, когда локальная переменная объявлена ​​и инициализирована в том же операторе; переменная не может быть инициализирована значением NULL, группой методов или анонимной функцией.
  • var нельзя использовать в полях в области класса.
  • Переменные, объявленные с помощью var, нельзя использовать в выражении инициализации. Другими словами, это выражение допустимо: int i = (i = 20); но это выражение вызывает ошибку времени компиляции: var i = (i = 20);
  • Несколько неявно типизированных переменных не могут быть инициализированы в одном операторе.
  • Если тип с именем var находится в области видимости, то ключевое слово var будет преобразовано в это имя типа и не будет рассматриваться как часть объявления неявно типизированной локальной переменной.

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

оборота lomaxx
источник
Определенно анонимные типы (новые {...}), а не неявно типизированные (var)
Марк Гравелл
Просто перечитайте то, что я опубликовал, и это было неправильно. Я имел в виду неявно типизированные переменные tho
lomaxx 05
1
Эрик Липперт объяснил, почему var нельзя использовать вне методов, потому что он создает черный ящик возможностей. blogs.msdn.com/ericlippert/archive/2009/01/26/…
Guvante
1
var не является вариантом! это как раз для стенографии (особенно анонимных типов). наслаждайтесь динамикой, когда она появляется ...
ShuggyCoUk