Які найбільші недоліки дизайну в C # або .NET Framework загалом?
Приклад: не існує типового типу рядка, який не можна онулювати, і вам потрібно перевірити DBNull під час отримання значень із IDataReader.
Які найбільші недоліки дизайну в C # або .NET Framework загалом?
Приклад: не існує типового типу рядка, який не можна онулювати, і вам потрібно перевірити DBNull під час отримання значень із IDataReader.
Відповіді:
Я рішуче погоджуюсь з цим постом (для тих, хто пукає відсутність ToString, існує атрибут налагоджувача, який забезпечує власний формат для вашого класу).
Зверху наведеного вище списку я б також додав такі обґрунтовані запити:
T : new(string)або деT : new(string, int)var e = new Foo(); e { Bar = baz };Either<T>", це не так, тому я хотів би якось оголосити закритий алгебраїчний тип і накласти на нього вичерпне узгодження шаблонів (в основному першокласна підтримка шаблону відвідувача, але набагато ефективніше); так що просто приймайте переліки, розширюйте їх за допомогою вичерпної підтримки збігу шаблонів і не допускайте недійсних випадківSystem.IOзаняття, як Stream, наприклад , дещо погано розроблені; будь-який інтерфейс, який вимагає деяких реалізацій, NotSupportedExceptionє поганим дизайном,IListмає бути набагато простішим, ніж є; насправді, це може бути справедливим для багатьох інтерфейсів збирання бетону, наприклад ICollection,INotifyPropertyChanged, які приймають ім'я поля як рядок; Ви можете зробити це, використовуючи метод розширення, який бере лямбду з a MemberExpression, тобто. () => Foo, але це не дуже ефективно,
nameof()оператор для імен окремих членів, але це не працює у nameof(T) == "T"загальних виробах ( замість фактичного імені аргументу типу: все ще потрібно це зробити typeof(T).Name)) - і не дозволяє отримати рядок "шлях" , наприклад, nameof(this.ComplexProperty.Value) == "Value"обмеження можливих застосувань.IArithmetic; можливі також інші корисні інтерфейси спільного оператора,readonlyключове слово, а в C # 6.0 додані автоматичні властивості лише для читання, хоча воно не таке суворе, як підтримка справжньої мови для незмінних типів і значень.Наразі цього вистачає. Це все роздратування, з якими я стикався минулого тижня. Я, мабуть, міг би тривати годинами, якби я справді до цього додумався. C # 4.0 вже додає іменовані, необов'язкові та аргументи за замовчуванням, що я наполегливо схвалюю.
Тепер щодо одного необґрунтованого запиту:
Гарненько, будь ласка? :-)
List<T>мільйон Ц. Як ви пропонуєте зробити знімок ефективно? # 21: Використовуйте readonlyключове слово .... хоча тут є кілька хороших пропозицій, вони в основному саме такі - пропозиції, а не недоліки дизайну.
Reset()метод на IEnumerator<T>була помилка (для блоків ітераторів, мова специфікація навіть вимагає , що це кидає виняток)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, але це могло бути зрозумілішеFoo(SqlConnection! connection)(який вводить null-check / throw) був би непоганим (на відміну від int?тощо)
dynamic, або ви можете ввімкнути це такforeachрозгортанні, що означає, що anon-методи / лямбди захоплюють одну змінну, а не одну за ітерацію (болюче при потоках / async / і т.д.)
ApplicationExceptionце помилка - не корисна, як вони сподівалися. Вони також сказали, що це System.Exceptionмало бутиabstract .
TextWriter - базовий клас StreamWriter. wtf?
Це мене завжди бентежить до крайності.
Невеликий конструктор C # pet peev - використовує синтаксис C ++ / Java, щоб конструктор мав те саме ім'я, що і клас.
New()або ctor()було б набагато приємніше.
Звичайно, такі інструменти, як coderush, роблять це менш проблематичним для перейменування класів, але завдяки доступності для читання POV, New () забезпечує велику ясність.
class Foo { new(int j) {i = j} int i; }
New- ключові слова верхнього регістру суперечать умовам), я вагаюся назвати це вадою дизайну. Вони хотіли залучити існуючих розробників C ++ / Java, і запозичення багатьох дурних старих синтаксичних конвенцій, безперечно, допомогло їм досягти своєї мети.
Я не розумію, що ти не можеш зробити
де T: новий (U)
Отже, ви заявляєте, що загальний тип T має конструктор, який не є типовим.
редагувати:
Я хочу зробити це:
public class A
{
public A(string text)
{
}
}
public class Gen<T> where T : new(string text)
{
}
Я справді здивований, що першим згадав про це:
Набори даних 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 не так багато, що викликає у мене відчуття, як команда розробників сказала: "Добре, хлопці, ми досить близько - доставляйте!" Але це точно так.
DBNull.Value, коли вона nullсама по собі була б цілком адекватною для представлення NULL. На щастя, LINQ-to-SQL просто використовує null для NULL.
Редагувати
5. Ще одне моє роздратування - це те, як System.Reflection.BindingFlags має різне використання залежно від використовуваного вами методу. Наприклад, у FindFields, що означає CreateInstance або SetField? Це випадок, коли вони перевантажили зміст цього переліку, що викликає заплутаність.
Я не знаю, що я піду так далеко, що скажу, що це недолік дизайну, але було б дуже добре, якби ви могли зробити висновок про лямбда-вираз так само, як і у 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 кожен персонаж має значення! Хіба вони не беруть цього до уваги, коли розробляють ці мови програмування? :)
(int x) => x * (x -1);може означати, Func<int, int>а може означатиExpression<Func<int, int>>
+не визначений для object.
Клас System.Object :
Equals і GetHashCode - не всі класи можна порівняти або розширити, їх слід перемістити в інтерфейс. IEquatable або IComparable (або подібний) спадає на думку.
ToString - не всі класи можна перетворити на рядок, їх слід перемістити в інтерфейс. IФормат (або подібний) приходить мені на думку.
ICollection.SyncRoot властивість:
Дженерики мали бути там із самого початку:
EqualityComparer<T>.Defaultправильно оновити . Тоді обидва var dict = new Dictionary<object, string>(EqualityComparer<object>.Default)і var dict = new Dictionary<object, string>()використовуватимуть порівняльне посилання / рівність.
EqualityComparer<T>.Defaultробить. Не потрібно перевіряти при кожному пошуку. Порівняльник є властивістю Dictionaryекземпляра, і кожен Dictionaryзнає, який саме він використовує.
Одна з речей, яка мене дратує, - це Predicate<T> != Func<T, bool>парадокс. Вони обидва делегатів типу, T -> boolі все ж вони не сумісні з призначенням.
Деякі люди (ISV) бажають, щоб ви могли скомпілювати його до машинного коду під час побудови та зв'язати, щоб створити власний виконуваний файл, якому не потрібен час роботи dotNet.
Ми так багато знаємо про право методики ОО. Роз'єднання, програмування за контрактом, уникнення неправильного успадкування, належне використання винятків, відкритий / закритий принципал, замінність Ліскова тощо. Однак у рамках .Net не застосовуються найкращі практики.
Для мене найбільшим недоліком дизайну .Net є не стояння на плечах гігантів; просування менш ідеальних парадигм програмування до маси програмістів, які використовують їх фреймворки .
Якби MS звернули на це увагу, у цьому десятилітті світ програмної інженерії міг би зробити значні стрибки з точки зору якості, стабільності та масштабованості, але, на жаль, це, здається, регресує.
Мені не подобається оператор перемикача C #.
Я хотів би щось подібне
switch (a) {
1 : do_something;
2 : do_something_else;
3,4 : do_something_different;
else : do_something_weird;
}
Тож більше немає перерв (легко забути) та можливості розділяти різні значення комами.
switchпринципово порушений у всіх мовах, які наслідують навмисно скалічену версію C (оптимізовану для швидкості!). VB коштує набагато краще, але все ще відстає від світлових років за мовами з узгодженням шаблонів (Haskell, F #…).
Події в C #, де вам потрібно чітко перевірити слухачів. Хіба не в цьому суть подій, щоб транслювати їх тому, хто випадково опинився там? Навіть якщо таких немає?
Жахлива (і досить невидима для більшості людей) поведінка O (N ^ 2) вкладених / рекурсивних ітераторів .
Мені дуже неприємно, що вони про це знають, знають, як це виправити але це не розглядається як таке, що має достатній пріоритет, щоб заслужити включення.
Я постійно працюю з деревоподібними структурами, і мені доводиться виправляти інакше розумні коди людей, коли вони ненавмисно вводять дуже дорогі операції таким чином.
Принадність "yield foreach" полягає в тому, що простіший, простіший синтаксис заохочує правильний, продуктивний код. Це "яма успіху", до якої, на мою думку, їм слід прагнути, перш ніж додавати нові функції для довгострокового успіху платформи.
Деякі класи реалізують інтерфейси, але вони не реалізують багато методів цього інтерфейсу, наприклад, Array реалізує IList, але 4 з 9 методів викидають NotSupportedException http://msdn.microsoft.com/en-us/library/system.array_members .aspx
Статичні члени та вкладені типи в інтерфейсах.
Це особливо корисно, коли член інтерфейсу має параметр типу, який є специфічним для інтерфейсу ( наприклад, an enum). Було б непогано вкласти тип enum у тип інтерфейсу.
Страшенно небезпечний характер подій за замовчуванням. Той факт, що ви можете викликати подію і знаходитись у суперечливому стані через вилучення передплатників, просто жахливий. Дивіться чудові статті Джона Скіта та Еріка Ліпперта, щоб більше прочитати цю тему.
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.
Кортежі повинні бути структурами, але структури непотрібно гальмують усунення хвостового виклику, тому один із найпоширеніших і основних типів даних буде розподіляти без потреби і руйнувати масштабований паралелізм.
IEnumerator?
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)
Щоб додати до довгого списку хороших балів, зроблених іншими вже:
DateTime.Now == DateTime.Now у більшості, але не у всіх випадках.
Stringякий є незмінним, має купу варіантів побудови та маніпуляцій, але StringBuilder(що змінюється) не має.
Monitor.Enterі Monitor.Exitмали бути методами екземпляра, тому замість того, щоб створювати конкретний об’єкт для блокування, ви можете створити a Monitorта заблокувати його.
Деструктори ніколи не повинні називатися деструкторами. Специфікація ECMA називає їх фіналізаторами, що набагато менш заплутано для натовпу C ++, але специфікація мови все ще називає їх деструкторами.
DateTime.Nowних є найбільш очевидним станом у світі, але +1 для решти
Те, як ми використовуємо властивості, мене часом дратує. Мені подобається думати про них як про еквівалент методів 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.
Методи розширення приємні, але це некрасивий спосіб вирішити проблеми, які можна було б вирішити чистіше за допомогою справжніх міксинів (подивіться на ruby, щоб зрозуміти, про що я говорю), на тему міксинів. Дійсно хорошим способом додати їх до мови було б дозволити використання дженериків для успадкування. Це дозволяє розширити існуючі класи приємним об'єктно-орієнтованим способом:
public class MyMixin<T> : T
{
// etc...
}
це можна використовувати так, щоб розширити рядок, наприклад:
var newMixin = new MyMixin<string>();
Це набагато потужніше, ніж методи розширення, оскільки дозволяє замінити методи, наприклад, обернути їх, дозволяючи функціональність, схожу на AOP, всередині мови.
Вибачте за розмову :-)
Microsoft не буде виправляти очевидні помилки у фреймворку і не надаватиме хуки, щоб кінцеві користувачі могли їх виправляти.
Крім того, не існує способу двійкового виправлення виконуваних файлів .NET під час виконання, а також неможливо вказати приватні версії бібліотек .NET framework без двійкового виправлення власних бібліотек (щоб перехопити виклик завантаження), а ILDASM не підлягає розповсюдженню, тому я не можу автоматизувати патч у будь-якому випадку.
Можливість викликати метод розширення для нульової змінної є спірною, наприклад
об'єкт a = null; a.MyExtMethod (); // це можна викликати, припустимо, десь він визначив MyExtMethod
Це може бути зручно, але неоднозначно щодо тем винятків нульових посилань.
Одна назва „вада”. 'C' "configuration" у System.configuration.dll має бути написано великими літерами.
Обробка винятків. Виняток слід примусово вловити або кинути, як у Java, компілятор повинен перевірити його під час компіляції. Користувачі не повинні покладатися на коментарі щодо інформації про винятки в межах цільового виклику.
Метод .Parameters.Add () на SqlCommand у V1 фреймворку був жахливо розроблений - одна з перевантажень в основному не працювала б, якщо ви передали параметр зі значенням (int) 0 - це призвело до їх створення метод .Parameters.AddWithValue () для класу SqlCommand.
ICollection<T>і IList<T>; як мінімум, коваріантний інтерфейс збору лише для читанняIListSource<out T> (з перелічувачем, індексатором та Count) був би надзвичайно корисним.Transform(Sequence<T>, Func<T,T>) функцію, яка мала швидко визначити, чи повертає функція те саме значення чи інше значення. Якщо функція не змінює більшість / усіх своїх аргументів, то вихідна послідовність може спільно використовувати частину / всю пам'ять із вхідної послідовності. Без можливості побітового порівняння будь-якого значення типу Т, слід застосовувати набагато повільніше порівняння, що надзвичайно шкодить продуктивності.List<T>до гіпотетичного IListSource<U>(де T: U), хоча клас явно не реалізує цей інтерфейс. Існує щонайменше три різні бібліотеки (написані незалежно), що забезпечують цю функціональність (звичайно, з недоліками продуктивності - якби було можливе ідеальне обхідне рішення, було б несправедливо називати це недоліком .NET).WeakReference<T>(ви можете легко написати свій власний, але він буде використовувати зліпки всередині.)Predicate<T>проти Func<T,bool>). Я часто хочу, щоб ми могли мати структурну типізацію інтерфейсів та делегатів, щоб досягти більш вільного зв’язку між компонентами, оскільки в .NET недостатньо для класів у незалежних бібліотеках DLL реалізувати один і той же інтерфейс - вони також повинні мати спільне посилання на третій DLL, що визначає інтерфейс.DBNull.Valueіснує, хоча nullб однаково добре виконував ті самі цілі.variable = variable ?? value. Справді, є кілька місць у C #, яким без потреби не вистачає симетрії. Наприклад, ви можете писати if (x) y(); else z();(без фігурних дужок), але не можете писати try y(); finally z();.IList<T>, хоча я б використовував IReadableByIndex<out T>і IAppendable<in T>. Багато інших ваших речей - це писки, я б також погодився.
IListReader<T>;) - я використовую слово "джерело" як антонім "тонути" (інтерфейс лише для запису).
IListSource<in T>або IReadableList<out T>. Значення того, що базові типи інтерфейсів включають методи, які існують не у всіх похідних, може бути корисним, хоча я думаю, що часто добре, щоб інтерфейси були дещо спеціалізованими. Наприклад, можна мати метод, IList<T>який містить методи зміни розміру, які можуть працювати або не працювати, і IResizableList<T>який реалізує ті самі методи, але гарантує, що вони повинні працювати. Такий підхід може бути корисним у випадках, коли поле може містити або єдине існуюче посилання на змінний список, або спільне посилання на незмінний.
if (rl:(list as IResizableList<T>) != null) rl.Add(...);, але є й інші пропозиції. Як автора різних колекцій та адаптерів колекцій, мене дратує написання багатьох фіктивних методів, які створюють винятки. Як типовий вентилятор безпеки, я не хочу, щоб мені дозволяли називати незаконні методи. Вболівальник IntelliSense, я не хочу бачити їх у списку.
Одна річ , яка галочкою мене в 1.x було при використанні System.Xml.XmlValidatingReader, то ValidationEventHandler«s ValidationEventArgsне виставляє Базового XmlSchemaException(позначено внутрішній) , який має всю корисну інформацію , як linenumberі position. Натомість вам слід проаналізувати це з властивості рядка Message або скористатися відображенням, щоб викопати його. Не так добре, коли потрібно повернути кінцевому користувачеві дезінфіковану помилку.
Не подобається, що ви не можете використовувати значення одного переліку в іншому, наприклад:
enum Colors { white, blue, green, red, black, yellow }
enum SpecialColors { Colors.blue, Colors.red, Colors.Yellow }
typeof(Color)! = typeof(SpecialColors).
enum SpecialColors { blue = Colors.blue, red = Colors.red, yellow = Colors.Yellow }
Неявно набрані змінні були імплементовані погано в IMO. Я знаю, що ви насправді повинні використовувати їх лише під час роботи з виразами Linq, але дратує те, що ви не можете оголосити їх поза локальною сферою.
З MSDN:
Причиною того, що я вважаю, що це погана реалізація, є те, що вони називають це var, але це далеко не варіант. Це насправді просто скорочений синтаксис для того, щоб не потрібно вводити повністю ім'я класу (за винятком випадків, коли використовується з Linq)