Недоліки дизайну C # (.NET) [закрито]


84

Які найбільші недоліки дизайну в C # або .NET Framework загалом?

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


В якому сенсі ці недоліки дизайну?
Джульєтта

За допомогою IDataReader ви можете використовувати IsDBNull, а не перевіряти вручну
Marc Gravell

9
Cue Jon Skeet поговорити про закриті заняття;)
johnc

3
Виправити IDataReader методом розширення досить просто: див. Weblogs.asp.net/skillet/archive/2008/06/18/… .
Роберт Росней,

@lagerdalek - я б поставив +1 цьому коментарю, якби міг; добре запам'ятався
Марк Гравелл

Відповіді:


39

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

Зверху наведеного вище списку я б також додав такі обґрунтовані запити:

  1. ненульовані посилальні типи як доповнення до типів значень, що допускають нульове значення,
  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. виправити різні настирливі випадки кутів у загальному дозволі перевантаження; наприклад, спробуйте надати два перевантажені методи розширення, один, який працює з посилальними типами, а інший - з типовими типами структури, і подивіться, як це подобається висновку вашого типу,
  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 можна було б застосувати, якщо невіртуальні методи / властивості могли бути оформлені, щоб вказати, що їх слід викликати без нульової перевірки. Це дозволило б таким речам, як поля "Рядок", поводитися так, ніби вони за замовчуванням порожній рядок, а не нуль (оскільки статична функція "довжина" рядка може повернути 0, якщо викликати його на нульовий рядок).
supercat

1
Що стосується №2, то загалом було б корисно, якби структури могли вказувати не тільки конструктор, відмінний від заливки нулем, але й конструктор копіювання, відмінний від байтової копії. Насправді, я б дуже хотів бачити .net-подібний фреймворк, який міг би добре виконати роботу, розпізнаючи, що сутності можуть мати значення або посилальну семантику, і дозволивши об'єкти купи типу значення мітити мінливими, спільно-незмінними або незв'язаними (незв'язаний об'єкт значення може бути мутованим, якщо його режим спочатку CompareExchange'd змінився, або може бути спільним, якщо його режим спочатку CompareExchange'd спільним).
supercat

1
Відмінні бали! Для №1 анотація системи типів є загальним рішенням, але я віддаю перевагу розповсюдженню обмежень конструктора за допомогою змінних типу, наприклад T: new (). Re: # 2, хороший момент щодо конструкторів копіювання, але я був би радий із більш загальними конструкторами, що описані вище. Ще краще було б повністю виключити конструктори-як-відмінні-методи і просто зробити їх статичними методами. Це дозволяє спростити і більш загальні шаблони побудови, особливо якщо ми дозволяємо статичні методи на інтерфейсах. Конструктори-як-статичні-методи + статичні-методи-в-інтерфейсах також вирішує # 1.
naasking

3
# 3: Який сенс використовувати герметичний клас як загальний параметр типу, наприклад Foo <T> де T: рядок? # 11: Гаразд, отже, я маю List<T>мільйон Ц. Як ви пропонуєте зробити знімок ефективно? # 21: Використовуйте readonlyключове слово .... хоча тут є кілька хороших пропозицій, вони в основному саме такі - пропозиції, а не недоліки дизайну.
Qwertie

2
Це дуже цікава відповідь, але я думаю, що нам слід оновити функції C # 6. Приклад: Елементи 19 та 21 були реалізовані =)
eduardobr

72
  • Reset()метод на IEnumerator<T>була помилка (для блоків ітераторів, мова специфікація навіть вимагає , що це кидає виняток)
  • на думку Еріка, методи відображення, які повертають масиви, були помилкою
  • коваріація масиву була і залишається дивним
    • Оновлення: 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 трохи допомагає , але недостатньо
  • жодна підтримка "цей параметр типу ref не може бути нульовим" - хоча контракти (у версії 4.0) дещо допомагають у цьому. Але синтаксис типу Foo(SqlConnection! connection)(який вводить null-check / throw) був би непоганим (на відміну від int?тощо)
  • відсутність підтримки операторів та конструкторів, що не виконуються за замовчуванням, із дженериками; C # 4.0 це трохи вирішує dynamic, або ви можете ввімкнути це так
  • ітераторна змінна оголошується поза межами while у foreachрозгортанні, що означає, що anon-методи / лямбди захоплюють одну змінну, а не одну за ітерацію (болюче при потоках / async / і т.д.)

IEnumerable! = IEnumerable <object> справді дивна
Раухоц,

2
Ну, річ IEnumerable - це похмілля 1,1; ви можете використовувати .Cast <object> () з LINQ, принаймні
Marc Gravell

8
Люди BCL сказали, що ApplicationExceptionце помилка - не корисна, як вони сподівалися. Вони також сказали, що це System.Exceptionмало бутиabstract .
Jay Bazuzi

2
Не нульовий: Помилкою компіляції має бути передача звичайного посилального типу T до чогось, що приймає ненульований T! (так само, як ви не можете передати int? to int). Пройти інший напрямок, звичайно, добре.
Jay Bazuzi

1
@ Джон Харроп: IMHO, повинна була бути підтримка незмінних масивів та посилань на масив, що лише для читання. Можливо, для деяких інших варіантів масиву (наприклад, "змінний масив" (непряме посилання) або посилальний масив із зміщенням та зв’язком).
supercat

60

TextWriter - базовий клас StreamWriter. wtf?

Це мене завжди бентежить до крайності.


19
+1 Щоразу мені доводиться це шукати. (Whaddaya означає, що я не можу створити TextWriter ()?)
Ніколас Пясецький

1
Слава Богу ... я думав, що це лише я.
IJ Kennedy

? Завжди щось пише текст, але лише StreamWriter робить це для потоку. Здається, досить просто.
Джон Ханна

4
Назва StreamWriter не робить факт написання тексту досить очевидним IMO. Це звучить ізольовано, як би просто писав байти, а TextWriter був би чудовою реалізацією зверху, яка перетворила б api (string toWrite) у байти для вас. Тепер, якби його називали StreamTextWriter, тоді, звичайно, це було б відразу очевидно, але трохи довге :(
Дивовижний

44

Невеликий конструктор C # pet peev - використовує синтаксис C ++ / Java, щоб конструктор мав те саме ім'я, що і клас.

New()або ctor()було б набагато приємніше.

Звичайно, такі інструменти, як coderush, роблять це менш проблематичним для перейменування класів, але завдяки доступності для читання POV, New () забезпечує велику ясність.


Е-м-м, як би він знав, для чого ви намагаєтеся створити новий екземпляр?
BlueRaja - Danny Pflughoeft

4
@BlueRaja: Скотт має на увазі іменування конструкторів у класах. class Foo { new(int j) {i = j} int i; }
dalle

Хоча я погоджуюсь на 100%, що ctor () або constructor () були б кращими (а не New- ключові слова верхнього регістру суперечать умовам), я вагаюся назвати це вадою дизайну. Вони хотіли залучити існуючих розробників C ++ / Java, і запозичення багатьох дурних старих синтаксичних конвенцій, безперечно, допомогло їм досягти своєї мети.
Кверті

пов'язане з цим питанням: stackoverflow.com/questions/32101993/c-sharp-sorted-linkedlist немає можливості (за допомогою лише .NET) просто сортувати LinkedList за допомогою MergeSort, bucketsort або будь-якого іншого алгоритму сортування, але за допомогою linq, який є набагато повільнішим ніж спеціальна реалізація.
CoffeDeveloper

29

Я не розумію, що ти не можеш зробити

де T: новий (U)

Отже, ви заявляєте, що загальний тип T має конструктор, який не є типовим.

редагувати:

Я хочу зробити це:

public class A 
{
    public A(string text) 
    {

    }
}


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

}

Загальний тип оголошує, як ви будете використовувати об'єкти цього типу, тобто їх інтерфейс. Конструктор - це деталь реалізації цього інтерфейсу, що не стосується споживача. Коли вам потрібно створити параметризований екземпляр, використовуйте фабрику.
Bryan Watts

Для інформації, хоча він не може виконати перевірку під час компіляції, у MiscUtil є деякий код для ефективного використання конструкторів, що не за замовчуванням (у загальних виробах), тобто без Activator.CreateInstance або відображення.
Марк Гравелл

Тому що це не має сенсу і викликає заплутаність у деяких цілях. Проте це може бути корисним при роботі з незмінними об'єктами.
Pop Catalin

7
Загалом, відсутність обмежень для членів дратує, так.
MichaelGG

3
амінь, я завжди цього хотів
Стів

20

Я справді здивований, що першим згадав про це:

Набори даних ADO.NET, що вводяться, не виставляють стовпці, що підлягають нульовому використанню, як властивості типів, що дозволяють опустити. Ви повинні мати можливість написати це:

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

Натомість вам потрібно написати це, що просто дурно:

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

Це дратувало в .NET 2.0, і це ще більше дратує зараз, коли вам доводиться використовувати джиггі-покер, як зазначено вище, у ваших приємних акуратних запитах LINQ.

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

У .NET не так багато, що викликає у мене відчуття, як команда розробників сказала: "Добре, хлопці, ми досить близько - доставляйте!" Але це точно так.


Я від усього серця згоден! Це змушує мене щоразу, коли мені доводиться цим користуватися (що часто буває). Ага! +1
Ейвінд

2
Найменше, що вони могли б зробити, - це придумати новий клас DataSetV2 (погане ім'я - лише заради аргументу), який використовував типи обнулюваних значень замість DBNull.
Крістіан Хейтер,

Не забуваємо про абсурдність вимагати особливої ​​цінності DBNull.Value, коли вона nullсама по собі була б цілком адекватною для представлення NULL. На щастя, LINQ-to-SQL просто використовує null для NULL.
Qwertie

Насправді цей абсурд - це скеля, на якій побудована вся абсурдна споруда.
Роберт Росней,

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

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 - не всі класи можна перетворити на рядок, їх слід перемістити в інтерфейс. IФормат (або подібний) приходить мені на думку.

  2. ICollection.SyncRoot властивість:

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

    • System.Collections простір імен містить багато більш-менш застарілих класів і інтерфейсів.

1
1. Ці методи настільки поширені, що було вирішено, що випадки диваків були нормальними, чи важливо, чи може хтось викликати Object.Equals у вашому класі? Відомо, що реалізація може бути, а може і не бути, і вимагаючи: IEquatable, IFormattable на 99% класів є непарним.
Гуванте,

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знає, який саме він використовує.
dalle

1
@supercat: Словник повинен використовувати найзагальніший тип (тобто загальний базовий клас) як ключ, використовуючи як Strings, так і DateTime в одному і тому ж словнику не буде робити жодного сенсу, якщо не використовуватиметься порівняння посилань, якщо не передбачено порівняння, яке визначає користувач, що є. Пам’ятайте, що назва цієї теми - «C # (.NET) Design Faws».
Далле

12

Одна з речей, яка мене дратує, - це Predicate<T> != Func<T, bool>парадокс. Вони обидва делегатів типу, T -> boolі все ж вони не сумісні з призначенням.


Існує хитрість із використанням Delegate.Create і деякого кастингу для перетворення, але принаймні мати можливість зробити явний акторський склад було б непогано (однак я можу зрозуміти відсутність підтримки неявного)
Гуванте

Структура делегатів загалом недосконала; наприклад, відсутність слабких подій (реалізація слабких подій на стороні джерела без особливих зусиль з боку абонента може бути здійснена лише за допомогою купу роздумів та ReflectionPermission, див. codeproject.com/Articles/29922/Weak-Events-in-C ), та неефективність, що випливає з вимоги, що делегати повинні бути еталонними типами (делегати були б швидшими і використовували б на 1/3 стільки пам'яті у багатьох випадках, якби вони були типами значень - тоді вони були б просто парою вказівники, які ви можете передати на стек.)
Кверті

11

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


Ви повинні мати можливість NGEN свою програму перед запуском.
Otávio Décio

Не те саме, що видалити залежності середовища виконання. Ви зберігаєте лише перший пробіг JIT над вашим кодом.
Ed S.


Чи не існує інструменту затуманення, який це робить? Він вбудує фреймворк у ваш exe, тому вам не доведеться його розгортати. Я не можу згадати це ім'я ... це не PreEmptive ...
JoshBerke

2
Хіба Postbuild Xenocode цього не робить? Було б непогано, якби Visual Studio мав спосіб зробити це ...
BenAlabaster

11

Ми так багато знаємо про право методики ОО. Роз'єднання, програмування за контрактом, уникнення неправильного успадкування, належне використання винятків, відкритий / закритий принципал, замінність Ліскова тощо. Однак у рамках .Net не застосовуються найкращі практики.

Для мене найбільшим недоліком дизайну .Net є не стояння на плечах гігантів; просування менш ідеальних парадигм програмування до маси програмістів, які використовують їх фреймворки .

Якби MS звернули на це увагу, у цьому десятилітті світ програмної інженерії міг би зробити значні стрибки з точки зору якості, стабільності та масштабованості, але, на жаль, це, здається, регресує.


4
+1 для цільової трансляції; я додав би до цього неможливість підкласу кожного класу, відсутність інтерфейсів для фундаментальних класів та небажання виправляти помилки фреймворку навіть через кілька років
Стівен А. Лоу

1
Якщо ми вирішимо переслідувати, чи ми говоримо, що рамки .Net настільки погані, що важко зупинитися на тому, який недолік найгірший? Я відчуваю себе краще після виходу з повітря і ціную підтримку, оскільки я очікував, що мене закричать хлопці-фанати MS.
Деніел Полл,

Я не голосував у будь-який спосіб, і в будь-якому випадку я дуже неохоче виставляю голоси проти, але я намагаюся зрозуміти, чому я ПРОСТО НЕ ДБУЮ.
Mike Dunlavey

2
Я думаю, ви кажете: "це не так добре, як могло б бути", що є невідповіддю. Ніщо не ідеально. Вкажіть конкретне.
jcollum

3
Ні, я кажу, що є численні випадки, коли дизайн, очевидно, має недоліки, тоді, коли ви думаєте, що вони все зрозуміли, вони все одно помиляються. Наприклад, мій допис на форумах MSDN тут: social.msdn.microsoft.com/forums/en-US/wpf/thread/…
Даніель Пол

11

Мені не подобається оператор перемикача C #.

Я хотів би щось подібне

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

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


Насправді я думаю, що було б ще краще, якби для цього знадобився або один вираз, або блок у фігурних дужках - як і все інше в C #. Без можливості пробитися, поточний синтаксис розриву трохи хитрий, і він також не обмежує сферу застосування. (AFAIK, можливо, я не знаю)
Тамаш Чінеге

Я не розумію, що ти маєш на увазі. Опублікуйте тут власний вираз про ідеальний перемикач. Я не хочу потрапляти, але значення, розділені комами.
tuinstoel

8
До речі, я згоден з ОП. switchпринципово порушений у всіх мовах, які наслідують навмисно скалічену версію C (оптимізовану для швидкості!). VB коштує набагато краще, але все ще відстає від світлових років за мовами з узгодженням шаблонів (Haskell, F #…).
Конрад Рудольф

1
tuinostel: щось на зразок switch (a) {case 1 {do_something; } випадок 2 {do_something_else; }} - тобто позбавлення від оператора break і вимагання належних блоків коду для кожного випадку
Tamas Czinege

2
Наявність перерви здається помилкою загалом, чи не помилка компіляції, щоб її видати? Здається, він є там лише для полегшення переходу від C до C # (він же навчає розробників, яким вони не можуть автоматично провалитися)
Гуванте,

10

Події в C #, де вам потрібно чітко перевірити слухачів. Хіба не в цьому суть подій, щоб транслювати їх тому, хто випадково опинився там? Навіть якщо таких немає?


1
Прикро, що для цього немає цукру, коли ти цього хочеш, я розумію, чому вони це роблять жорстко, закликає не
наводити

Хм Я або не розумію, або не погоджуюсь, або обидва :-). Я не можу сказати, що коли-небудь мене засмучувало щось робити. Для мене це пахне передчасною оптимізацією.
Thomas Eyde

Часткові методи є життєздатною альтернативою, в деяких випадках.
Роберт Харві,

ПЛЮС все, що це спричиняє ... У MS є CAB, який виправляє обидві ці проблеми, але CAB має багато власних проблем через обмеження в C # (наприклад, рядки, а не перелічення як теми подій) - чому б просто не робити вільно -coupled події частина мови !?
BlueRaja - Danny Pflughoeft

9

Жахлива (і досить невидима для більшості людей) поведінка O (N ^ 2) вкладених / рекурсивних ітераторів .

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

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

Принадність "yield foreach" полягає в тому, що простіший, простіший синтаксис заохочує правильний, продуктивний код. Це "яма успіху", до якої, на мою думку, їм слід прагнути, перш ніж додавати нові функції для довгострокового успіху платформи.


Це особливо помилкове, оскільки це суперечить інтуїтивним очікуванням поведінки О, тому багато людей
потраплять у

7

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


Ну, ви не можете змінити кількість елементів у масиві, тому нічого не може зробити Add, Clear, Insert та Remove (At), окрім як кинути NotSupported ... Насправді, я очікував би БУДЬ-якої реалізації IList, яка повертає true для IsFixedSize кине на них.
CB

@CB трохи запізнився на вечірку, я думаю :) Але якщо Array не може задовольнити "IList", навіщо це впроваджувати? Це порушення L у принципі SOLID.
Вінгер Сендон

7

Статичні члени та вкладені типи в інтерфейсах.

Це особливо корисно, коли член інтерфейсу має параметр типу, який є специфічним для інтерфейсу ( наприклад, an enum). Було б непогано вкласти тип enum у тип інтерфейсу.


1
Хіба це не дуже схоже на вашу іншу пропозицію?
RCIX

1
Ні, ця мова стосується мови C #, а інша - про фреймворк. Не всі дбають про різницю, тож дозвольте мені сказати, що ця стосується дозволеного, а інша - передбаченого.
Джей Базузі,

6

Страшенно небезпечний характер подій за замовчуванням. Той факт, що ви можете викликати подію і знаходитись у суперечливому стані через вилучення передплатників, просто жахливий. Дивіться чудові статті Джона Скіта та Еріка Ліпперта, щоб більше прочитати цю тему.


Я не заперечував би, якби за замовчуванням події не були безпечними для потоків (це може підвищити продуктивність однопотокового коду); що дурне, так це те, що додавання / видалення безпечно за замовчуванням, але що природний спосіб активувати подію небезпечний, і немає можливості легко зробити її безпечною.
Кверті

@Qwertie: Що дурніше, так це те, що впродовж деякого часу додавання / видалення використовувало б блокування, але все одно не було безпечним для потоків.
supercat

6
  • null скрізь.

  • const нікуди.

  • API несумісні, наприклад, мутація масиву повертається, voidале додавання до StringBufferповертає ту саму змінну StringBuffer.

  • Інтерфейси збору несумісні з незмінними структурами даних, наприклад, Addin 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.

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


Структурне введення тексту в CLR відсутнє, але, схоже, у вас структурне введення змішане з висновком типу або з функцією Ruby, відомою як "символи". Структурна типізація була б такою, якби CLR вважав Func <int, bool> та Predicate <int> однаковими або принаймні неявно конвертованими.
Qwertie

Говорячи про порівняння, не забувайте Comparer <T>!
Кверті

@Qwertie Я мав на увазі такі функції мови програмування, як поліморфні варіанти в OCaml. Бібліотека LablGL OCaml має безліч цікавих прикладів структурного набору тексту, корисного в контексті графіки. Нічого спільного з висновком типу і лише дотично пов'язаним із символами.
JD

1
Як користуватися незмінною структурою IEnumerator?
суперкіт

5

0 місячне освітлення як перерахування

особливості переліку: 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

моя пропозиція, використовуйте знак "@":

замість:

if ((myVar & MyEnumName.ColorRed)! = 0)

використовуй це:

if ((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 - Danny 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 та неявний конструктор, щоб робити подібні речі, зокрема, коли ви не хочете, щоб серіалізатор просто викликав властивості. Хоча трохи більше роботи, схоже, ви вже виконуєте більшу частину цієї роботи за допомогою свого методу.
Гуванте

4

Методи розширення приємні, але це некрасивий спосіб вирішити проблеми, які можна було б вирішити чистіше за допомогою справжніх міксинів (подивіться на ruby, щоб зрозуміти, про що я говорю), на тему міксинів. Дійсно хорошим способом додати їх до мови було б дозволити використання дженериків для успадкування. Це дозволяє розширити існуючі класи приємним об'єктно-орієнтованим способом:

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

це можна використовувати так, щоб розширити рядок, наприклад:

var newMixin = new MyMixin<string>();

Це набагато потужніше, ніж методи розширення, оскільки дозволяє замінити методи, наприклад, обернути їх, дозволяючи функціональність, схожу на AOP, всередині мови.

Вибачте за розмову :-)


5
Цікаво, але я віддаю перевагу методам розширення. Якщо я отримую бібліотеку, яка містить купу методів розширення для рядків, я не хочу змінювати всі посилання на рядки на MyMixin <string>, щоб отримати нові речі. Звичайно, це незначно, але саме прозоре додавання методів робить методи розширення такими приємними.
RCIX

До речі, чи знаєте ви, що це вже працює?
RCIX

2
Я не розумію, як LINQ міг би працювати таким чином
BlueRaja - Danny Pflughoeft

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
Які очевидні помилки фреймворку ви маєте на увазі?
Роберт Росней,

1
# 1 Клацніть на частково видимий дочірній елемент керування, що прокручується. Елемент керування переміщується у поле зору перед тим, як він отримує подію MouseDown, внаслідок чого клацання знаходиться десь у контролі, ніж очікувалося. Гірше на деревних видах, де це також запускає операцію перетягування.
Джошуа


3
  • Можливість викликати метод розширення для нульової змінної є спірною, наприклад

    об'єкт a = null; a.MyExtMethod (); // це можна викликати, припустимо, десь він визначив MyExtMethod

    Це може бути зручно, але неоднозначно щодо тем винятків нульових посилань.

  • Одна назва „вада”. 'C' "configuration" у System.configuration.dll має бути написано великими літерами.

  • Обробка винятків. Виняток слід примусово вловити або кинути, як у Java, компілятор повинен перевірити його під час компіляції. Користувачі не повинні покладатися на коментарі щодо інформації про винятки в межах цільового виклику.


3
Хоча дуже зручно - у мене є метод розширення ThrowIfNull для перевірки параметрів
;-p

2
ти можеш це зробити? тьфу ThrowIfNull це цікаве розширення, але це просто здається неправильним.
JoshBerke

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> (з перелічувачем, індексатором та Count) був би надзвичайно корисним.
  2. .NET не підтримує слабких делегатів . Обхідні шляхи в кращому випадку незграбні, а обхідні шляхи для слухачів неможливі при частковій довірі (потрібен ReflectionPermission).
  3. Уніфікація загального інтерфейсу заборонена, навіть коли це має сенс і не викликає проблем.
  4. На відміну від С ++, коваріантні типи повернення заборонені в .NET
  5. Побітове порівняння двох типів значень для рівності неможливе. У функціональній " постійній " структурі даних я писав aTransform(Sequence<T>, Func<T,T>) функцію, яка мала швидко визначити, чи повертає функція те саме значення чи інше значення. Якщо функція не змінює більшість / усіх своїх аргументів, то вихідна послідовність може спільно використовувати частину / всю пам'ять із вхідної послідовності. Без можливості побітового порівняння будь-якого значення типу Т, слід застосовувати набагато повільніше порівняння, що надзвичайно шкодить продуктивності.
  6. Здається, .NET не може ефективно підтримувати спеціальні інтерфейси (як ті, що пропонуються в Go або Rust). Такі інтерфейси дозволили б вам перейти List<T>до гіпотетичного IListSource<U>(де T: U), хоча клас явно не реалізує цей інтерфейс. Існує щонайменше три різні бібліотеки (написані незалежно), що забезпечують цю функціональність (звичайно, з недоліками продуктивності - якби було можливе ідеальне обхідне рішення, було б несправедливо називати це недоліком .NET).
  7. Інші проблеми з продуктивністю: IEnumerator вимагає двох викликів інтерфейсу за ітерацію. Звичайні вказівники методів (відкриті делегати розміру IntPtr) або делегати зі значенням (IntPtr * 2) неможливі. Масиви фіксованого розміру (довільного типу T) не можуть бути вбудовані всередину класів. Немає WeakReference<T>(ви можете легко написати свій власний, але він буде використовувати зліпки всередині.)
  8. Той факт, що ідентичні типи делегатів вважаються несумісними (відсутність неявного перетворення), для мене часом дратував (наприклад, Predicate<T>проти 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. Мене часто кусає непотрібна взаємозв'язок між методами отримання і встановлення: у похідному класі або похідному інтерфейсі ви не можете просто додати сеттер, коли базовий клас або базовий інтерфейс має лише геттер; якщо ви перевизначите геттер, то вам не дозволяється визначати сеттер; і ви не можете визначити сеттер як віртуальний, але геттер як невіртуальний.

Я погоджуюся з вами щодо підмножин IList<T>, хоча я б використовував IReadableByIndex<out T>і IAppendable<in T>. Багато інших ваших речей - це писки, я б також погодився.
supercat

Це справді довга назва. Можливо, ми могли б піти на компроміс IListReader<T>;) - я використовую слово "джерело" як антонім "тонути" (інтерфейс лише для запису).
Кверті

Можливо, IListSource<in T>або IReadableList<out T>. Значення того, що базові типи інтерфейсів включають методи, які існують не у всіх похідних, може бути корисним, хоча я думаю, що часто добре, щоб інтерфейси були дещо спеціалізованими. Наприклад, можна мати метод, IList<T>який містить методи зміни розміру, які можуть працювати або не працювати, і IResizableList<T>який реалізує ті самі методи, але гарантує, що вони повинні працювати. Такий підхід може бути корисним у випадках, коли поле може містити або єдине існуюче посилання на змінний список, або спільне посилання на незмінний.
supercat

У такому випадку код, який хоче змінити вміст списку, перевірить, чи це тип, що змінюється, а якщо ні, то генерує новий змінний екземпляр, що містить ті самі елементи, що і незмінний список, а потім почне використовувати його. Було б неприємно, якби коду доводилося постійно вводити поле в поле кожного разу, коли він хотів би використовувати для нього мутуючий метод.
supercat

@supercat Це лише неприємно, тому що C # не забезпечує дійсно простий спосіб перевірити, чи реалізований інтерфейс, і негайно використовувати його. Держави-члени повинні додати функцію мови, щоб полегшити її. Моїм улюбленим прийомом буде обов’язковий вираз if (rl:(list as IResizableList<T>) != null) rl.Add(...);, але є й інші пропозиції. Як автора різних колекцій та адаптерів колекцій, мене дратує написання багатьох фіктивних методів, які створюють винятки. Як типовий вентилятор безпеки, я не хочу, щоб мені дозволяли називати незаконні методи. Вболівальник IntelliSense, я не хочу бачити їх у списку.
Кверті

2

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


1

Не подобається, що ви не можете використовувати значення одного переліку в іншому, наприклад:

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

    enum SpecialColors { Colors.blue, Colors.red, Colors.Yellow } 

2
Однак це навіть не має сенсу. typeof(Color)! = typeof(SpecialColors).
Kirk Woll

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)


Безумовно анонімні типи (нові {...}), не неявно введені (var)
Марк Гравелл

Просто перечитайте те, що я розмістив, і це було неправильно. Я мав на увазі неявно набрані змінні tho
lomaxx

1
Ерік Ліпперт пояснив, чому var не можна використовувати поза методами, це в основному тому, що він створює чорний ящик можливостей. blogs.msdn.com/ericlippert/archive/2009/01/26/…
Гуванте,

1
var не призначений як варіант! це саме для стенографії (особливо нетипових типів). насолоджуйтесь динамікою, коли це відбувається ...
ShuggyCoUk
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.