Недостатки дизайна C# (.NET)

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

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

В каком смысле эти недостатки дизайна?

Juliet 05.01.2009 02:30

С IDataReader вы можете использовать IsDBNull вместо проверки вручную

Marc Gravell 05.01.2009 02:42

Скажите Джону Скиту, чтобы он поговорил о закрытых классах;)

johnc 05.01.2009 02:57

Исправить IDataReader с помощью метода расширения довольно просто: см. weblogs.asp.net/skillet/archive/2008/06/18/….

Robert Rossney 05.01.2009 11:00

@lagerdalek - Я бы добавил +1 к этому комментарию, если бы мог; хорошо помнят

Marc Gravell 05.01.2009 11:26

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

Guvante 12.02.2009 00:16

Этот вопрос субъективен и аргументирован. См .: stackoverflow.com/questions/6145786/…

Eduardo 27.05.2011 21:08

В VS обычно не установлен флажок check for arithmetic overflows. Это может позволить вам забыть об ошибках в случае, если вы имеете дело с большими числами.

nawfal 03.01.2013 06:38
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
84
8
12 567
32

Ответы 32

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

Вы должны иметь возможность NGEN для вашей программы перед запуском.

Otávio Décio 05.01.2009 02:35

Не то же самое, что удаление зависимостей среды выполнения. Вы сохраняете только первый запуск JIT вашего кода.

Ed S. 05.01.2009 02:40

См. yoda.arachsys.com/csharp/faq/#framework.required

Marc Gravell 05.01.2009 02:45

Разве не существует инструмента для обфускации, который это делает? Он встроит фреймворк в ваш исполняемый файл, поэтому вам не нужно его развертывать. Я не могу вспомнить его название ... это не PreEmptive ...

JoshBerke 05.01.2009 03:00

Разве Postbuild Xenocode не делает этого? Было бы неплохо, если бы у Visual Studio был способ сделать это ...

BenAlabaster 05.01.2009 03:21

Ага, а сколько стоит Xenocode? ЕСЛИ какая-то сторонняя компания может его построить, что мешает MS создать его и сделать частью VS?

Cyril Gupta 05.01.2009 03:51

У Mono есть компоновщик ... не пробовал с .NET: mono-project.com/Linker

Mark Brackett 05.01.2009 17:06

Xenocode очень удобен для всех своих возможностей. Около 400 долларов. Думаю, с 30-дневной пробной версией.

jcollum 06.01.2009 01:57

как ни странно, Mono делает это! tirania.org/blog/archive/2008/Nov-05.html

gbjbaanb 13.01.2009 22:09

Думал можно при установке NGEN? Я могу ошибаться ...

Christian Hayter 11.06.2009 16:22
  • метод 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 помогает немного, но недостаточно
  • нет поддержки «этот параметр типа ссылки не может быть нулевым» - хотя контракты (в 4.0) в некоторой степени помогают в этом. Но синтаксис вроде Foo(SqlConnection! connection) (который вводит нулевую проверку / throw) был бы хорош (в отличие от int? и т. д.)
  • отсутствие поддержки операторов и нестандартных конструкторов с дженериками; C# 4.0 немного решает эту проблему с помощью dynamic, или вы можете включить его как это
  • переменная итератора объявляется за пределами в то время как в расширении foreach, что означает, что анон-методы / лямбда-выражения захватывают единственную переменную, а не одну на итерацию (болезненно с потоковой / асинхронной / и т. д.)

IEnumerable! = IEnumerable <объект> действительно странный

Rauhotz 05.01.2009 02:49

Что ж, IEnumerable - это пережиток версии 1.1; вы можете использовать .Cast <object> () с LINQ, по крайней мере

Marc Gravell 05.01.2009 02:50

Ребята из BCL сказали, что ApplicationException был ошибкой - не полезен, как они надеялись. Они также сказали, что System.Exception должен был быть abstract.

Jay Bazuzi 05.01.2009 06:42

Не допускающий значения NULL: передача обычного ссылочного типа T в объект, который принимает не допускающий значение NULL T, должна вызывать ошибку! (точно так же, как вы не можете передать int? в int). Конечно, можно перейти в другую сторону.

Jay Bazuzi 05.01.2009 06:47

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

AnthonyWJones 09.01.2009 16:30

@AnthonyWJones - действительно. И если он точно описывает ошибку, какой цели он послужит? Затем исходный код должен перехватить FooException системы, FooException app-a и FooException app-b без непрерывности.

Marc Gravell 09.01.2009 16:41

FWIW, то, что Эрик Липперт написал в цитированном вами сообщении в блоге, - полная чушь. Нет ничего плохого в возврате массивов. Массивы могут быть неизменяемыми или изменяемыми. Массивы часто идеальны для параллелизма, а чисто функциональные структуры данных - отстой для параллелизма. Настоящий недостаток дизайна, стоящий за проблемой, которую он поднял, заключается в том, что .NET облажался, когда дело дошло до структур данных.

J D 14.02.2010 20:49

@Jon Harrop: IMHO, должна была быть поддержка неизменяемых массивов и ссылок на массивы только для чтения. Возможно, и для некоторых других вариантов массива (например, «массив с изменяемым размером» (косвенная ссылка) или ссылка на массив со смещением и границей).

supercat 19.11.2010 04:03

@Jon - почему-то я пропустил твой предыдущий комментарий. Скажем так, (и не в первый раз) у нас есть разногласия.

Marc Gravell 19.11.2010 07:33

@supercat: Совершенно верно. Что - настоящий недостаток дизайна в .NET.

J D 22.11.2010 12:15

Насколько я понимаю, IEnumerable.Reset() существует исключительно для того, чтобы соответствовать IEnumVARIANT в COM. Лично я, как человек, ненавидевший его со времен IEnumVARIANT, считаю его присутствие упущенной возможностью.

Jon Hanna 13.01.2012 03:27

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

Я согласен, но я думаю, вы имеете в виду метод SqlCommand.Parameters.Add ().

Matt Peterson 03.03.2009 20:30
  • Уметь вызывать расширение метод для нулевой переменной является спорным например

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

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

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

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

Впрочем, очень удобно - у меня есть метод расширения ThrowIfNull для проверки параметров ;-p

Marc Gravell 05.01.2009 02:51

ты можешь сделать это? тьфу ThrowIfNull это интересное расширение, но это кажется неправильным.

JoshBerke 05.01.2009 03:21

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

Pop Catalin 05.01.2009 03:26

Я немного об этом рассказывал в blog.freakcode.com/2008/09/hazards-of-extension-methods.html

Markus Olsson 05.01.2009 04:24

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

Bryan Watts 05.01.2009 07:08

Да, согласен и очень удобно.

Ray Lu 06.01.2009 00:43

исключение совершенно неверно. Вы не можете быстро потерпеть неудачу, если ВЫ ДОЛЖНЫ перехватить каждое чертово исключение, которое может быть выброшено в стек вызовов. Но я ДЕЙСТВИТЕЛЬНО хотел бы, чтобы было намного проще идентифицировать любые исключения, которые могут быть выброшены в конкретном вызове и его результирующем стеке вызовов ...

user1228 09.01.2009 16:53

@ Я искренне согласен, проверенные исключения просто заставляют разработчиков чаще добавлять "throws Exception". И в большинстве реальных случаев это вызывает большую двусмысленность, чем без него.

Guvante 12.02.2009 00:42

@Will: Обработка исключений неприятна как в Java, так и в .net, поскольку используемый механизм тесно связывает вместе три концепции, которые в некоторой степени связаны, но также в некоторой степени ортогональны: (1) Какие типы вещей пошли не так (ошибка границ массива, тайм-аут ввода-вывода и т. д.); (2) Должен ли определенный код действовать в результате; (3) В какой момент проблема должна считаться «решенной». Рассмотрим процедуру, которая должна изменять объект данными, считываемыми из IEnumerable. Что должно произойти, если при обработке этого IEnumerable возникнет исключение?

supercat 24.11.2011 00:37

@Will: По сути, у вызывающего абонента есть как минимум два ортогональных вопроса: (1) повлияла ли каким-либо образом оценка IEnumerable на состояние системы и (2) в какой степени неполное поведение мутирующего имеет или не повлиял на мутируемый объект, и мог ли он каким-либо образом повредить состояние системы за пределами этого объекта. Невозможно предоставить оба типа информации обратно вызывающей стороне, если только код, использующий IEnumerable, не имеет средства узнать, какие исключения из него представляют собой внешнее повреждение состояния.

supercat 24.11.2011 00:51

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

Из MSDN:

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

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

Определенно анонимные типы (новые {...}), а не неявно типизированные (var)

Marc Gravell 05.01.2009 07:56

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

lomaxx 06.01.2009 01:48

Эрик Липперт объяснил, почему var нельзя использовать вне методов, потому что он создает черный ящик возможностей. blogs.msdn.com/ericlippert/archive/2009/01/26/…

Guvante 12.02.2009 00:52

var не является вариантом! это как раз для стенографии (особенно для анонимных типов). наслаждайтесь динамикой, когда она появляется ...

ShuggyCoUk 21.02.2009 18:56

Не знаю, могу ли я сказать, что это недостаток дизайна, но было бы здорово, если бы вы могли вывести лямбда-выражение так же, как в 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 каждый персонаж чертовски важен! Разве они не принимают это во внимание при разработке этих языков программирования? :)

Microsoft должна учитывать Code Golf при разработке языков?

jrcs3 05.01.2009 03:14

MS должна учитывать код VB при разработке C# - в VB довольно много «хороших функций», которые отсутствуют в C#!

gbjbaanb 05.01.2009 03:43

@gbjbaanb: Проблема в их логике: «Программистам на C# не нужны эти функции VB, потому что они не похожи на все, что они хотели исторически». Поэтому они сохранили все приятные функции для непуристых программистов VB: P

BenAlabaster 05.01.2009 04:06

Это шутка? Как компилятор узнает, что вы имели в виду Func <int, int> вместо, например, Func <double, double>?

Ray Burns 03.11.2009 02:05

@ Рэй Бернс: Как это узнать в VB? VB поддерживает это, так в чем разница?

BenAlabaster 03.11.2009 02:52

@RayBurns Вывод типа? Я использую это с 1989 года.

RD1 02.12.2009 19:10

Ламды гомоиконны в C#. фрагмент (int x) => x * (x -1); может означать Func<int, int> или может означать Expression<Func<int, int>>

Scott Weinstein 13.04.2011 04:32

@BenAlabaster: VB поддерживает арифметические операторы с поздним связыванием. C# должен разрешить их во время компиляции. Это разница в языке. Например, VB может складывать два объекта вместе. C# не может, потому что + не определен для object.

recursive 29.02.2012 20:34

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

где T: new (U)

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

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

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

public class A 
{
    public A(string text) 
    {

    }
}


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

}

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

Bryan Watts 05.01.2009 08:10

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

Marc Gravell 05.01.2009 11:28

Потому что это не имеет смысла и в некоторых случаях сбивает с толку. Тем не менее, это может быть полезно при работе с неизменяемыми объектами.

Pop Catalin 05.01.2009 11:43

Это просто дало мне представление ... Вероятно, вы могли бы добиться этого, используя новую функцию Code Contracts в C# 4.0. Просто добавьте это ограничение в свой конструктор.

davogones 01.02.2009 08:57

В целом, да, отсутствие ограничений на членство раздражает.

MichaelGG 12.02.2009 00:21

аминь, я всегда хотел этого

Steve 21.02.2009 19:26

Для чего бы вы это использовали? Вы же не знаете, что означают параметры, верно?

Jouke van der Maas 08.05.2011 23:48
  1. Я не большой поклонник классов Stream, StringWriter, StringReader, TextReader, TextWriter ... просто не понятно, что к чему.
  2. IEnumerable.Reset генерирует исключение для итераторов. У меня есть некоторые сторонние компоненты, которые всегда вызывают сброс при привязке данных, и для их использования требуется, чтобы я сначала привел их к списку.
  3. Сериализатор Xml должен иметь сериализованные элементы IDictionary
  4. Я совершенно забыл о HttpWebRequest и FTP API, какая боль у меня .... (спасибо за комментарий Николас, чтобы напомнить мне об этом :-)

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

+1 Мне каждый раз приходится искать любой из классов XmlTextWriter, TextWriter и т. д. То же самое с материалом HttpWebRequest / Response. Совершенно неинтуитивно понятный API.

Nicholas Piasecki 05.01.2009 08:18

+ 1-1 = 0: XmlTextWriter и т. д., Я согласен, что невозможно точно вывести, что они из названия. HttpWebRequest Я не согласен, мне это кажется довольно интуитивным.

AnthonyWJones 09.01.2009 16:40

Полагаю, каждому свое. Я думаю, что с FTP я ожидал бы более высокого уровня абстракции, чем то, что есть у них.

JoshBerke 09.01.2009 21:29

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

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

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

Эмм, как он узнает, что вы пытаетесь создать новый экземпляр?

BlueRaja - Danny Pflughoeft 28.04.2010 19:15

@BlueRaja: Скотт имеет в виду именование конструкторов в классах. class Foo { new(int j) {i = j} int i; }

dalle 28.04.2010 22:28

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

Qwertie 04.02.2012 03:10

связанный с этим вопросом: stackoverflow.com/questions/32101993/c-sharp-sorted-linkedli‌ ул. нет способа (используя только .NET) просто отсортировать LinkedList с помощью MergeSort, bucketsort или любого другого алгоритма сортировки, но с использованием linq, который намного медленнее, чем специальная реализация.

CoffeDeveloper 19.08.2015 20:53

Некоторые классы реализуют интерфейсы, но они не реализуют многие методы этого интерфейса, например, 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 бросит на них.

C.B. 25.04.2013 17:53

@ C.B. немного опоздал на вечеринку, я думаю :) Но если Array не может удовлетворить "IList", зачем вообще его реализовывать? Это нарушение принципа L в SOLID.

wingerse 28.08.2017 17:47

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

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

Разве это не похоже на другое ваше предложение?

RCIX 30.06.2009 06:12

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

Jay Bazuzi 30.06.2009 07:24

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

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

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

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

Steven A. Lowe 05.01.2009 07:23

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

Daniel Paull 05.01.2009 08:09

Я не голосовал в любом случае, и в любом случае я очень не хочу отдавать голоса против, но я пытаюсь понять, почему мне ПРОСТО НЕ ЗАБОТИТЬСЯ.

Mike Dunlavey 05.01.2009 16:40

Я думаю, вы говорите: «Это не так хорошо, как могло бы быть», что не является ответом. Ничто не идеально. Укажите конкретику.

jcollum 06.01.2009 02:01

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

Daniel Paull 06.01.2009 02:28

@Will: вы джентльмен и ученый.

Daniel Paull 10.01.2009 03:19

Это правда! Я пришел в мир C# из Java и Kotlin. Когда я вижу, как они реализованы здесь в выражениях C# Range и самом типе Range, я буквально хочу спрыгнуть с моста. Вы только посмотрите, как были разработаны типы и интерфейсы Kotlin. Не могу поверить, что в Microsoft не могли придумать такие простые и широко используемые конструкции.

Michael P 18.08.2020 18:51

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

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

Какие очевидные ошибки фреймворка вы имеете в виду?

Robert Rossney 05.01.2009 10:29

# 1 Щелкните частично видимый дочерний элемент управления прокручиваемого элемента управления. Элемент управления перемещается в представление до того, как он получит событие MouseDown, в результате чего щелчок будет в другом месте элемента управления, чем ожидалось. Хуже в древовидных представлениях, где также запускается операция перетаскивания.

Joshua 05.01.2009 20:05

# 2 это # 3 это # 4 это

BlueRaja - Danny Pflughoeft 28.04.2010 20:35

Честно говоря, я должен сказать, что за годы программирования .NET (C#) у меня не было недостатков в конструкции фреймворка, которые я помнил; Это означает, что в моем случае, вероятно, нет недостатков, о которых стоит помнить.

Однако есть кое-что, что мне не нравилось пару лет назад, когда Microsoft выпускала XNA, они полностью отказались от своей версии MDX 2.0, что сделало мои игры неиграбельными и их нелегко было просто конвертировать. Это более широкий недостаток, не имеющий ничего общего с .NET-фреймворком.

.NET-framework на самом деле следует многим рекомендациям по дизайну Very Good, разработанным многими высокопроизводительными языковыми архитектурами. Так что я должен сказать, что я доволен .NET.

Но чтобы сказать вам кое-что, что могло бы быть лучше, мне пришлось бы пожаловаться на общую систему, я не нахожу универсальных для интерфейсов, таких как «где T - это MyObj» (это не совсем правильный синтаксис. Однако эта часть можно было бы сделать намного лучше и понятнее.

Представьте, что у вас есть интерфейс, который разделяют 2 разных класса, если вам нужен общий метод внутри этого интерфейса, вам нужно пройти через какой-то неприятный Generics-sytanx. Возможно, я просто хочу делать странные вещи. Только памятная вещь для меня.

Иногда меня раздражает то, как мы используем свойства. Мне нравится думать о них как о эквиваленте методов 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.

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

Guvante 12.02.2009 00:37

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 enum - одна из немногих вещей, которые Java сделала правильно, а C# - нет.

BlueRaja - Danny Pflughoeft 28.04.2010 20:28

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

Типизированные наборы данных 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

Eyvind 09.01.2009 17:16

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

Christian Hayter 11.06.2009 16:20

Давайте не будем забывать абсурдность требования специального значения DBNull.Value, когда самого null было бы вполне достаточно для представления NULL. К счастью, LINQ-to-SQL просто использует null вместо NULL.

Qwertie 04.02.2012 03:01

На самом деле этот абсурд и есть скала, на которой построено все абсурдное здание.

Robert Rossney 05.02.2012 04:34

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

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

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

var newMixin = new MyMixin<string>();

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

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

Интересно, но я предпочитаю методы расширения. Если я получаю библиотеку, содержащую кучу методов расширения для строк, я не хочу, чтобы мне приходилось менять все строковые ссылки на MyMixin <string>, чтобы получить новый материал. Конечно, это мелочь, но именно прозрачное добавление методов делает методы расширения такими хорошими.

RCIX 30.06.2009 06:07

Кстати, знаете ли вы, что это уже работает?

RCIX 30.06.2009 06:16

Я не понимаю, как LINQ может работать таким образом

BlueRaja - Danny Pflughoeft 28.04.2010 20:17

@RCIX: Миксины звучат так, как я думал, должны работать методы расширения. Проблема с созданием неявных методов расширения заключается в том, что это означает, что реальные члены класса должны иметь приоритет над методами расширения. Если определен метод расширения Graphics.DrawParallelogram (Pen p, Point v1, Point v2, Point v3), а затем в System.Graphics добавлена ​​функция DrawParallelogram, которая использует точки в другом порядке, код, использующий метод расширения, сломается без предупреждение. Кстати, возникла бы проблема с использованием двух точек для методов расширения (например, object..method ()?)

supercat 19.11.2010 03:56
  1. Класс System.Object:

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

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

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

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

    • Пространство имен System.Collections содержит множество более или менее устаревших классов и интерфейсов.

Что касается ToString (), он вызывается отладчиком для всех объектов, поэтому в любом случае он должен быть в System.Object.

Joshua 05.01.2009 20:37

Значит, ToString () должен существовать из-за отладчика? Отладчику не требуется ToString (), он может использовать IFormattable или просто выводить ClassName 0xaddress, если объект не реализует IFormattable.

dalle 05.01.2009 21:41

1. Эти методы настолько распространены, что было решено, что со странностями все в порядке, имеет ли значение, может ли кто-то вызвать Object.Equals в вашем классе? Известно, что реализация может быть или не быть, и требование: IEquatable, IFormattable для 99% классов является нечетным.

Guvante 12.02.2009 00:45

2. Чем внешняя блокировка отличается от SyncRoot? Если вы возражаете против оператора naked lock (), вы правы, есть много лучших альтернатив, но он выполняет свою работу. 3. Хорошее замечание

Guvante 12.02.2009 00:47

Я согласен с Гуванте: общее правило - всегда делайте общий случай по умолчанию, и классы, в которых полезны .ToString() / .Equals(), - это, безусловно, обычный случай.

BlueRaja - Danny Pflughoeft 28.04.2010 19:20

Полезно иметь возможность, например, создать словарь с определяемыми пользователем объектами в качестве ключей, используя равенство ссылок по умолчанию, без необходимости явно добавлять код к определяемым пользователем объектам для этой цели. Я бы счел Finalize гораздо большей тратой (лучшей альтернативой было бы иметь объекты, которые потребуют финализации, реализовывать iFinalizable и явно регистрировать себя для завершения). OTOH, должно было быть больше встроенной поддержки iDisposable, включая вызов Dispose, если конструктор выдает исключение.

supercat 19.11.2010 03:50

@supercat: Все, что нужно, - это правильно обновить EqualityComparer<T>.Default. Тогда и var dict = new Dictionary<object, string>(EqualityComparer<object>.Default), и var dict = new Dictionary<object, string>() будут использовать эталонное сравнение / равенство.

dalle 19.11.2010 09:23

@dalle: Проверять при каждом поиске, поддерживает ли ключевой класс для словаря iEquatable, было бы дорого. С другой стороны, система может предоставить статический метод GetEqualityComparer <T> (T thing), который будет возвращать либо «вещь» (если она поддерживает iEquatable), либо метод сравнения ссылок, если нет. Это нужно было бы вызывать только один раз при создании Dictionary, а не один раз для каждого поиска.

supercat 19.11.2010 18:50

@supercat: То, что вы описываете, - это именно то, что делает EqualityComparer<T>.Default. Нет необходимости проверять каждый поиск. Компаратор - это свойство экземпляра Dictionary, и каждый Dictionary знает, какой из них он использует.

dalle 19.11.2010 22:16

@dalle: Если подумать, если решение о том, рассматривать ли ключи как iEquatable, было принято при создании коллекции, это могло иметь странные последствия, если бы подтип iEquatable был добавлен в коллекцию базового типа, отличного от iEquatable (например, наполнение " Строка "s в" Dictionary <Object> "). Можно было бы возразить, что лучше, чтобы все объекты либо использовали ссылочное равенство, либо iEquatable (без явного iEqualityComparator), чем иметь переопределения Equals и GetHashCode для каждого экземпляра, но до того, как дженерики появились на сцене, как можно было бы красиво кодировать для этого?

supercat 20.11.2010 00:43

@supercat: словарь должен использовать наиболее общий тип (то есть общий базовый класс) в качестве ключа, использование как Strings, так и DateTime в одном словаре не будет иметь никакого смысла, если не используется сравнение ссылок, если только определенное пользователем сравнение не доказано, что является. Помните, что название этой темы - «Недостатки дизайна C# (.NET)».

dalle 20.11.2010 11:50

До появления .net 2.0 каждый словарь использовал объект в качестве ключа; никто не мог знать, будут ли объекты поддерживать iEquatable, если только один из них не проверил каждый тип во время выполнения или не имел параметра в конструкторе Dictionary, чтобы сказать об этом. Наличие у каждого объекта поддержки Equals и GetHashCode позволяет избежать этой проблемы. Кроме того, независимо от того, действительно ли это нужно делать, можно добавить несколько очень разных типов ключей в словарь с четко определенной семантикой. Если бы не все типы поддерживали Equals и GetHashCode, это было бы невозможно (к лучшему или к худшему).

supercat 21.11.2010 00:42

@supercat: Вы все еще этого не видите. Dictionary<Key, Value> может содержать ключи только типа Key или типов, производных от Key. Все, что знает словарь, существует в базовом типе Key. Я сказал, что «Дженерики должны были быть там с самого начала». Если бы это было так, это не было бы проблемой.

dalle 21.11.2010 14:54

@dalle: Мы согласны с тем, что отсутствие дженериков стало причиной многих неудачных решений. Однако я бы сказал, что GetHashCode / Equals был доступен только для объектов iEquatable, были бы случаи, когда наилучшее использование коллекции потребовало бы опроса отдельных элементов для iEquatable. Как еще, например, можно было бы поддерживать коллекцию уникальных объектов iDisposable, имея в виду, что структура может реализовывать iDisposable? Может случиться так, что такие случаи могут быть достаточно редкими, чтобы затраты времени выполнения таких сравнений были бы приемлемыми, но это все равно некрасиво.

supercat 21.11.2010 20:08

@dalle: Кстати, как скорость доступа к переопределенному методу базового класса сравнивается со скоростью доступа к интерфейсу? Насколько я понимаю, доступ к методу базового класса немного быстрее. Не большая разница, но тесты на равенство часто выполняются в циклах, которые выполняются много раз.

supercat 21.11.2010 20:10

@supercat: Если вас беспокоит скорость, взгляните на Dictionary<TKey, TValue> с использованием .NET Reflector. Он уже использует интерфейс для всех сравнений, интерфейс IEqualityComparer<TKey>.

dalle 21.11.2010 22:05

TextWriter - это класс база StreamWriter. что за хрень?

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

+1 Мне каждый раз приходится это искать. (Whaddaya иметь в виду Я не могу новый TextWriter ()?)

Nicholas Piasecki 06.01.2009 02:02

Слава богу ... Я думал, что это только я.

I. J. Kennedy 18.12.2010 06:08

? Всегда что-то записывает текст, но только StreamWriter делает это с потоком. Кажется довольно простым.

Jon Hanna 13.01.2012 03:28

Название StreamWriter не делает тот факт, что он пишет текст, достаточно очевидным IMO. Это звучит изолированно, как если бы он просто записывал байты, а TextWriter был бы причудливой реализацией поверх, которая преобразовывала бы для вас api (string toWrite) в байты. Теперь, если бы он назывался StreamTextWriter, тогда, конечно, это было бы сразу очевидно, но немного длинновато :(

Quibblesome 31.01.2012 22:35

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

Есть трюк с использованием Delegate.Create и некоторого приведения для преобразования, но, по крайней мере, было бы хорошо иметь возможность выполнить явное приведение (однако я могу понять отсутствие поддержки неявного)

Guvante 12.02.2009 00:48

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

Qwertie 04.02.2012 02:32

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

CLR (и, следовательно, C#) не поддерживает множественное наследование, а ASP.NET забит разрывами LSP ...

Это мои "любимые" ...

Я, наверное, смог бы найти больше ошибок, но это те, которые мне не нравятся больше всего ... !! :(

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

Guvante 12.02.2009 00:50

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

RD1 02.12.2009 19:32

@Guvante: Я бы сказал, что 95% времени он обрабатывается с интерфейсами. Однако с методами расширения интерфейсов а также он обрабатывается в 100% случаев. Ура!

BlueRaja - Danny Pflughoeft 28.04.2010 22:58

множественное наследование полезно и является одной важной отсутствующей функцией языка C#. Однако правильная реализация требует устранения перегрузки метода. Действительно, правильная реализация MI устраняет все предполагаемые недостатки MI и упрощает повторное использование кода через наследование без какого-либо стандартного кода, удовлетворяющего компилятор.

Kemal Erdogan 14.03.2013 12:58

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

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

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

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

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

DateTime.Now - наиболее очевидное состояние гонки в мире, но +1 для остальных.

BlueRaja - Danny Pflughoeft 28.04.2010 22:49

Дело не столько в том, что это состояние гонки, сколько в том, что они сделали это свойством. Свойства выглядят точно так же, как поля, поэтому это довольно удивительное поведение IMO.

Brian Rasmussen 29.04.2010 10:53

@Brian Rasmussen: DateTime.Now вполне правильно является свойством, поскольку оно изменяется не при чтении, а под воздействием внешних факторов. Если вы читаете такое свойство, как SomeForm.Width, а затем - после того, как пользователь изменил размер формы - читаете его снова, значение при втором чтении будет другим. Хотя возможно, что выполнение первого DateTime.Now может занять достаточно много времени, чтобы повлиять на значение, считываемое вторым, такой эффект не будет отличаться от любой другой функции, выполнение которой заняло такое же время.

supercat 23.11.2011 23:27

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

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

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

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

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

Tamas Czinege 24.01.2009 16:16

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

tuinstoel 24.01.2009 16:35

DrJokepu: провалы существуют даже для непустых предложений. Вам нужно только сделать это явным, используя goto case.

Konrad Rudolph 24.01.2009 21:09

Кстати, я согласен с ОП. switch в корне сломан на всех языках, которые эмулируют намеренно урезанную версию C (оптимизированную для скорости!). VB работает намного лучше, но все еще на световые годы отстает от языков с сопоставлением с образцом (Haskell, F#…).

Konrad Rudolph 24.01.2009 21:12

Конрад: Вы, конечно, правы насчет goto - и, вероятно, это причина того, что до сих пор используется синтаксис переключателя C в C#.

Tamas Czinege 25.01.2009 00:55

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

Tamas Czinege 25.01.2009 00:58

В общем, наличие break кажется ошибкой, разве это не ошибка компиляции? Кажется, это только для облегчения перехода с C на C# (он же учит разработчиков, что они не могут автоматически провалиться)

Guvante 12.02.2009 00:41

Здесь, здесь! Сделайте общий случай по умолчанию!

BlueRaja - Danny Pflughoeft 28.04.2010 20:19

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

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

ShuggyCoUk 21.02.2009 18:57

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

Thomas Eyde 22.02.2009 00:05

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

Robert Harvey 29.05.2009 20:36

ПЛЮС все связи, которые это вызывает ... У MS есть CAB, который устраняет обе эти проблемы, но у CAB есть много собственных проблем из-за ограничений в C# (например, строки, а не перечисления как темы событий) - почему бы просто не сделать слабосвязанные события частью языка !?

BlueRaja - Danny Pflughoeft 28.04.2010 20:20

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

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

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

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

Это особенно ошибочно, потому что оно противоречит интуитивным ожиданиям от поведения О и поэтому попадет в ловушку для многих людей.

oefe 21.02.2009 19:32

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

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

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

Но это даже не имеет смысла. typeof(Color)! = typeof(SpecialColors).

Kirk Woll 01.06.2011 01:09

Сделать это достаточно просто: enum SpecialColors { blue = Colors.blue, red = Colors.red, yellow = Colors.Yellow }

Trystan Spangler 13.06.2011 20:52

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

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

Qwertie 04.02.2012 01:57

@Qwertie: Что еще глупее, так это то, что некоторое время при добавлении / удалении использовалась блокировка, но она все еще не была потокобезопасной.

supercat 07.07.2012 01:01
  • 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.

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

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

Qwertie 04.02.2012 02:07

Говоря о сравнении, не забывайте Comparer <T>!

Qwertie 04.02.2012 02:08

@Qwertie Я имел в виду такие особенности языка программирования, как полиморфные варианты в OCaml. В библиотеке OCaml LablGL есть много интересных примеров использования структурной типизации в контексте графики. Ничего общего с выводом типа и только косвенно связано с символами.

J D 05.02.2012 17:29

Как можно использовать неизменяемую структуру IEnumerator?

supercat 07.07.2012 01:00

Классы StreamWriter и StreamReader (и подклассы) закрывают базовый поток в Close () и Dispose (). Не могу сосчитать, сколько раз я работал над этим выбором дизайна.

Правильный дизайн заключался бы в предоставлении вариант для определения того, должен ли StreamReader или StreamWriter взять на себя владение базовым потоком, поскольку в некоторых случаях это должно происходить, а в других - нет.

supercat 07.07.2012 00:57
  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> против 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 07.07.2012 00:56

Это действительно длинное имя. Возможно, мы могли бы пойти на компромисс с IListReader<T>;) - Я использую слово «источник» как антоним слова «сток» (интерфейс только для записи).

Qwertie 18.07.2012 23:16

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

supercat 18.07.2012 23:25

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

supercat 18.07.2012 23:27

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

Qwertie 22.08.2012 03:03

С описанным вами шаблонным сценарием можно довольно легко справиться без новой языковой конструкции: {var rl=list as IResizeableList<T>; if (rl!=null)...; проблема в том, что это требует бокса. Более интересным был бы способ сказать: «Вызвать метод с параметрами с общими ограничениями для универсального объекта, если это возможно, или выполнить какое-либо другое действие, если нет». Я думаю, что компилятор мог бы сделать это без изменений во время выполнения, если бы он создавал общий статический класс для каждой такой ситуации. Например (с использованием удобочитаемых имен) MaybeDisposer<T> с общедоступным статическим методом Dispose(ref T item) ...

supercat 22.08.2012 22:32

... который вызовет делегата DisposeIt. Первоначально этот делегат будет настроен на подпрограмму, которая будет проверять, реализует ли T (параметр универсального типа класса) IDisposable; в противном случае он установил бы для делегата режим бездействия; в противном случае он использовал бы Reflection для создания делегата для вызова подпрограммы с параметром, ограниченным до IDisposable. Стоимость вызова такой процедуры после первого использования с любым конкретным типом будет равна стоимости одного вызова через статический делегат. Никаких изменений во время выполнения .net не потребуется.

supercat 22.08.2012 22:36

Решением проблемы «надоедливых фиктивных методов» было бы позволить интерфейсу IFoo обозначать статический класс IFooHelpers, так что если .NET загружает класс, который утверждает, что реализует IFoo, но не имеет int fnord(string st), среда выполнения автоматически генерирует int IFoo.fnord(string st) { return IFooHelpers.ClassHelpers.fnord(this, st); }. Добавьте средство для интерфейса, чтобы указать строгие имена других интерфейсов, с которыми он должен считаться совместимым, и многие давние проблемы могут быть легко решены.

supercat 30.06.2015 22:55

Другие вопросы по теме