Каковы некоторые из самых больших недостатков дизайна на С# или .NET Framework в целом?
Пример: нет типа нулевой строки, и вы должны проверить DBNull при извлечении значений из IDataReader.
Каковы некоторые из самых больших недостатков дизайна на С# или .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
, которые принимают имя поля в виде строки; Вы можете сделать это, используя метод расширения, который принимает лямбда с MemberExpression
, т.е. () => Foo
, но это не очень эффективно,
nameof()
для имен отдельных членов, но он не работает в обобщенных выражениях (nameof(T) == "T"
вместо действительного имени аргумента типа: вам все еще нужно сделать typeof(T).Name
)) - и при этом он не позволяет вам получить строку "путь", например, nameof(this.ComplexProperty.Value) == "Value"
ограничение его возможных применений.IArithmetic
; возможны и другие полезные интерфейсы общих операторов,readonly
, а в С# 6.0 добавлены авто-свойства только для чтения, хотя он не такой строгий, как поддержка истинного языка для неизменяемых типов и значений.Этого достаточно на данный момент, я полагаю. Это все раздражения, с которыми я столкнулся на прошлой неделе. Я мог бы, вероятно, продолжать часами, если бы действительно думал об этом. С# 4.0 уже добавляет именованные, необязательные и стандартные параметры, которые я категорически одобряю.
Теперь для одного необоснованного запроса:
Довольно пожалуйста? :-)
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)
(который вводит нулевую проверку /throw
), был бы хорош (в отличие от int?
и т.д.)
dynamic
, или вы можете включить его следующим образомforeach
, что означает, что anon-method/lambdas захватывает одну переменную, а не одну за итерацию (болезненно с threading/async/etc)
TextWriter - это базовый класс StreamWriter. WTF?
Это всегда смущает меня до крайности.
В небольших конструкторах С# pet peev используются синтаксис С++/Java с тем, чтобы конструктор был тем же именем, что и класс.
New()
или ctor()
было бы намного приятнее.
И конечно, такие инструменты, как coderush, делают это менее проблемой для переименования классов, но из читаемости POV, New() обеспечивает большую ясность.
Я не понимаю, что вы не можете сделать
где T: new (U)
Итак, вы заявляете, что общий тип T имеет конструктор, отличный от стандартного.
изменить
Я хочу сделать это:
public class A
{
public A(string text)
{
}
}
public class Gen<T> where T : new(string text)
{
}
Изменить
5. Еще одно мое раздражение - это то, как System.Reflection.BindingFlags имеет разные применения в зависимости от метода, используемого вами. В FindFields, например, что означает CreateInstance или SetField? Это случай, когда они перегружают значение этого перечисления, которое запутывает.
Я очень удивлен, что я первый, кто упомянул об этом:
Наборы данных ADO.NET не отображают столбцы с нулевым значением как свойства типов с нулевым значением. Вы должны иметь возможность написать это:
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 не так много, что заставляет меня чувствовать, что команда разработчиков сказала: "Ладно, мальчики, мы достаточно близко, отправляем!" Но это точно.
Я не знаю, что я бы сказал, что это ошибка дизайна, но было бы очень приятно, если бы вы могли вывести выражение лямбда так же, как вы можете в VB:
VB:
Dim a = Function(x) x * (x - 1)
С#
Было бы неплохо, если бы это можно было сделать:
var a = x => x * (x - 1);
Вместо этого:
Func<int, int> a = x => x * (x - 1);
Я понимаю это не намного дольше, но в Code Golf каждый персонаж считается чертовски! Разве они не учитывают это при разработке этих языков программирования?:)
System.Object класс:
Equals и GetHashCode - не все классы сопоставимы или хешируются, их следует перенести в интерфейс. IEwatable или IComparable (или подобное) приходит на ум.
ToString - не все классы могут быть преобразованы в строку, должны быть перемещены в интерфейс. IFormattable (или подобное) приходит на ум.
ICollection.SyncRoot свойство:
В начале должны существовать общие функции:
Одна из вещей, которая меня раздражает, - парадокс Predicate<T> != Func<T, bool>
. Они оба являются делегатами типа T -> bool
, и все же они не совместимы с назначением.
Некоторые люди (ISV) хотят, чтобы вы могли скомпилировать его для машинного кода во время сборки и связать его, чтобы создать собственный исполняемый файл, который не требует времени выполнения dotNet.
Мне не нравится оператор С# switch.
Мне хотелось бы что-то вроде этого
switch (a) {
1 : do_something;
2 : do_something_else;
3,4 : do_something_different;
else : do_something_weird;
}
Таким образом, больше нет разрывов (легко забыть) и возможность запятой - разделять разные значения.
Мы так много знаем о методах right OO. Развязка, программирование по контракту, исключение ненадлежащего наследования, правильное использование исключений, открытый/закрытый принцип, замещаемость Лискова и т.д. Тем не менее, инфраструктура .NET не использует лучшие практики.
Для меня самый большой недостаток в дизайне .Net не стоит на плечах гигантов; продвигает менее идеальные парадигмы программирования в массы программистов, которые используют свои рамки.
Если бы MS обратила на это внимание, мир программного обеспечения мог бы сделать большие скачки в плане качества, стабильности и масштабируемости в этом десятилетии, но, увы, он, похоже, регрессирует.
События на С#, где вы должны явно проверить слушателей. Разве это не было связано с событиями, чтобы транслировать, кто бы там ни был? Даже если их нет?
Ужасный (и совершенно невидимый для большинства людей) O (N ^ 2) поведение вложенных/рекурсивных iterators.
Я очень потрошен, что они знают об этом, знаю как его исправить, но он не рассматривается как имеющий достаточный приоритет, чтобы заслужить включение.
Я все время работаю с подобными дереву структурами и должен исправлять иначе умный код людей, когда они непреднамеренно вводят дорогостоящие операции таким образом.
Красота "yield foreach" заключается в том, что более простой и простой синтаксис способствует правильному, результативному коду. Это "успех" что я думаю, что они должны стремиться, прежде чем добавлять новые функции для долгосрочного успеха платформы.
Некоторые классы реализуют интерфейсы, но не реализуют многие из методов этого интерфейса, например, Array реализует IList, но 4 из 9 методов бросают NotSupportedException http://msdn.microsoft.com/en-us/library/system.array_members.aspx
Статические элементы и вложенные типы в интерфейсах.
Это особенно полезно, когда член интерфейса имеет параметр типа, специфичного для интерфейса (например, enum
). Было бы неплохо вложить тип перечисления в тип интерфейса.
Жестко опасный по умолчанию характер событий. Тот факт, что вы можете вызывать событие и находиться в противоречивом состоянии из-за удаления подписчиков, просто ужасен. См. и
null
.
const
нигде.
API несовместимы, например. мутация массива возвращает void
, но добавление к StringBuffer
возвращает тот же изменчивый StringBuffer
.
Интерфейсы коллекции несовместимы с неизменяемыми структурами данных, например. Add
в System.Collections.Generic.IList<_>
не может вернуть результат.
Нет структурной типизации, поэтому вы пишете System.Windows.Media.Effects.SamplingMode.Bilinear
вместо просто Bilinear
.
Mutable 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
.
Кортежи должны быть структурами, но структуры без необходимости блокируют устранение хвостового вызова, поэтому один из наиболее распространенных и фундаментальных типов данных будет излишне распределять и уничтожать масштабируемые parallelism.
0 moonlighting as 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
мое предложение, положите знак "@" на хорошее использование:
вместо:
if ((myVar и MyEnumName.ColorRed)!= 0)
используйте это:
if ((myVar и MyEnumName.ColorRed)!= @0)
Чтобы добавить к длинному списку хороших точек, сделанных другими:
DateTime.Now == DateTime.Now
в большинстве, но не во всех случаях.
String
, который является неизменным, имеет множество вариантов построения и манипуляции, но StringBuilder
(который изменен) не работает.
Monitor.Enter
и Monitor.Exit
должны были быть методами экземпляра, поэтому вместо того, чтобы вводить определенный объект для блокировки, вы можете создать новый Monitor
и заблокировать его.
Деструкторы никогда не должны были называться деструкторами. Спецификация ECMA называет их финализаторами, что гораздо менее запутанно для толпы С++, но спецификация языка по-прежнему относится к ним как к деструкторам.
То, как мы используем свойства, иногда раздражает меня. Мне нравится думать о них как о эквиваленте методов 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.
Удлинительные методы хороши, но они являются уродливым способом решения проблем, которые могли быть решены с помощью чистых реальных миксов (посмотрите на рубин, чтобы увидеть, о чем я говорю), по теме mixins. Действительно хороший способ добавить их к языку - это разрешить использование дженериков для наследования. Это позволяет расширить существующие классы с помощью объектно-ориентированного подхода:
public class MyMixin<T> : T
{
// etc...
}
это можно использовать, например, для расширения строки, например:
var newMixin = new MyMixin<string>();
Это гораздо более мощное средство, чем методы расширения, поскольку оно позволяет вам переопределять методы, например, чтобы обернуть их, что позволяет AOP-подобным функциям внутри языка.
Извините за разговор: -)
Метод .Parameters.Add() в SqlCommand в V1 структуры был ужасно разработан - одна из перегрузок в основном не работала бы, если бы вы передали параметр со значением (int) 0 - это привело для них создается метод .Parameters.AddWithValue() в классе SqlCommand.
Возможность вызова расширения метод по переменной null возможен например.
объект a = null; a.MyExtMethod();//это вызываемое, предположим, что где-то он определил MyExtMethod
Это может быть удобно, но он неоднозначен в вопросах исключения ссылочных ссылок.
Один из названий "недостаток". "C" "конфигурации" в System.configuration.dll следует заглавными буквами.
Обработка исключений. Исключение должно быть принудительно поймано или выброшено, как в Java, компилятор должен проверить его во время компиляции. Пользователи не должны полагаться на комментарии для информации об исключениях в целевом вызове.
Microsoft не будет исправлять очевидные ошибки в структуре и не будет предоставлять перехваты, чтобы конечные пользователи могли их исправить.
Кроме того, нет возможности для исполняемых файлов на двоичном патче .NET во время выполнения и никоим образом не указывать частные версии библиотек .NET Framework без двоичного исправления собственных библиотек (для перехвата вызова загрузки), а ILDASM не распространяется, поэтому Я все равно не могу автоматизировать патч.
ICollection<T>
и IList<T>
; как минимум, ковариантный интерфейс для чтения только IListSource<out T>
(с перечислителем, индексом и Count) был бы чрезвычайно полезен.Transform(Sequence<T>, Func<T,T>)
, которая должна была быстро определить, возвращает ли функция тот же значение или другое значение. Если функция не изменяет большинство/всех своих аргументов, то последовательность вывода может совместно использовать некоторую/всю память из входной последовательности. Без возможности побитового сравнения любого значения типа T следует использовать гораздо более медленное сравнение, которое сильно ухудшает производительность.List<T>
гипотетическим IListSource<U>
(где T: U), даже если класс явно не реализует этот интерфейс. Есть, по крайней мере, три разные библиотеки (написано самостоятельно) для обеспечения этой функциональности (с недостатками производительности, конечно - если бы было возможно прекрасное обходное решение, было бы несправедливо называть это недостаток в .NET).WeakReference<T>
(вы можете легко написать свой собственный, но он будет использовать броски внутри.)Predicate<T>
vs Func<T,bool>
). Я часто хотел бы, чтобы структурная типизация для интерфейсов и делегатов, чтобы добиться более слабой связи между компонентами, потому что в .NET этого недостаточно для классов в независимых DLL для реализации того же интерфейса - они также должны совместно использовать общую ссылку на третью DLL, которая определяет интерфейс.DBNull.Value
существует, хотя null
одинаково хорошо служил бы той же цели.variable = variable ?? value
. Действительно, в С# есть несколько мест, которые не нуждаются в симметрии. Например, вы можете написать if (x) y(); else z();
(без фигурных скобок), но вы не можете написать try y(); finally z();
.Одна вещь, которая меня отключила в 1.x, заключалась в использовании System.Xml.XmlValidatingReader
, ValidationEventHandler
ValidationEventArgs
не отображала базовую XmlSchemaException
(помеченную внутреннюю), которая имеет всю полезную информацию, такую как linenumber
и position
. Вместо этого вы должны проанализировать это из свойства строки Message или использовать отражение, чтобы выкапывать его. Не так хорошо, когда вы хотите вернуть более дезинфицированную ошибку конечному пользователю.
Не нравится, что вы не можете использовать значения одного перечисления в другом перечислении, например:
enum Colors { white, blue, green, red, black, yellow }
enum SpecialColors { Colors.blue, Colors.red, Colors.Yellow }
Неявно Типизированные переменные были реализованы слабо ИМО. Я знаю, что вы действительно должны использовать их только при работе с выражениями Linq, но это раздражает, что вы не можете объявлять их за пределами локальной области.
Из MSDN:
Причина, по которой я думаю, что это плохая реализация, заключается в том, что они называют это var, но это далеко не вариант. Это действительно просто сокращенный синтаксис для того, чтобы не вводить полное имя класса (кроме случаев, когда оно используется с Linq)