C# (.NET) 设计缺陷

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/411906/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 02:23:04  来源:igfitidea点击:

C# (.NET) Design Flaws

c#.net

提问by Rauhotz

What are some of the biggest design flaws in C# or the .NET Framework in general?

C# 或 .NET Framework 中的一些最大设计缺陷是什么?

Example: there is no non-nullable string type and you have to check for DBNull when fetching values from an IDataReader.

示例:没有不可为空的字符串类型,您必须在从 IDataReader 获取值时检查 DBNull。

采纳答案by naasking

I agree emphatically with this post(for those poo-pooing the lack of ToString, there is a debugger attribute to provide a custom format for your class).

我强烈同意这篇文章(对于那些因缺少 ToString 而大便的人,有一个 debugger 属性可以为您的类提供自定义格式)。

On top of the above list, I would also add the following reasonable requests:

在上述列表的顶部,我还将添加以下合理的要求:

  1. non-nullable reference types as a complement to nullable value types,
  2. allow overriding a struct's empty constructor,
  3. allow generic type constraints to specify sealed classes,
  4. I agree with another poster here that requested arbitrary constructor signatures when used as constraints, ie. where T : new(string), or where T : new(string, int)
  5. I also agree with another poster here about fixing events, both for empty event lists and in the concurrent setting (though the latter is tricky),
  6. operators should be defined as extension methods, and not as static methods of the class (or not just as static methods at least),
  7. allow static properties and methods for interfaces (Java has this, but C# does not),
  8. allow event initialization in object initializers (only fields and properties are currently allowed),
  9. why is the "object initializer" syntax only usable when creating an object? Why not make it available at any time, ie. var e = new Foo(); e { Bar = baz };
  10. fix quadratic enumerable behaviour,
  11. all collections should have immutable snapshots for iteration (ie. mutating the collection should not invalidate the iterator),
  12. tuples are easy to add, but an efficient closed algebraic type like "Either<T>" is not, so I'd love some way to declare a closed algebraic type and enforce exhaustive pattern matching on it (basically first-class support for the visitor pattern, but far more efficient); so just take enums, extend them with exhaustive pattern matching support, and don't allow invalid cases,
  13. I'd love support for pattern matching in general, but at the very least for object type testing; I also kinda like the switch syntax proposed in another post here,
  14. I agree with another post that the System.IOclasses, like Stream, are somewhat poorly designed; any interface that requires some implementations to throw NotSupportedExceptionis a bad design,
  15. IListshould be much simpler than it is; in fact, this may be true for many of the concrete collection interfaces, like ICollection,
  16. too many methods throw exceptions, like IDictionary for instance,
  17. I would prefer a form of checked exceptions better than that available in Java (see the research on type and effect systems for how this can be done),
  18. fix various annoying corner cases in generic method overload resolution; for instance, try providing two overloaded extension methods, one that operates on reference types, and the other on nullable struct types, and see how your type inference likes that,
  19. provide a way to safely reflect on field and member names for interfaces like INotifyPropertyChanged, that take the field name as a string; you can do this by using an extension method that takes a lambda with a MemberExpression, ie. () => Foo, but that's not very efficient,
    • Update: C# 6.0 added the nameof()operator for single member names, but it doesn't work in generics (nameof(T) == "T"instead of the actual type-argument's name: you still need to do typeof(T).Name)) - nor does it allow you to get a "path" string, e.g. nameof(this.ComplexProperty.Value) == "Value"limiting its possible applications.
  20. allow operators in interfaces, and make all core number types implement IArithmetic; other useful shared operator interfaces are possible as well,
  21. make it harder to mutate object fields/properties, or at the very least, allow annotating immutable fields and make the type checker enforce it (just treat it as getter-only property fer chrissakes, it's not hard!); in fact, unify fields and properties in a more sensible way since there's no point in having both; C# 3.0's automatic properties are a first step in this direction, but they don't go far enough,
    • Update: While C# had the readonlykeyword, and C# 6.0 added read-only auto-properties, though it isn't as stringent as true language support for immutable types and values.
  22. simplify declaring constructors; I like F#'s approach, but the other post here that requires simply "new" instead of the class name is better at least,
  1. 不可为空的引用类型作为可空值类型的补充,
  2. 允许覆盖结构的空构造函数,
  3. 允许泛型类型约束指定密封类,
  4. 我同意这里的另一张海报,它在用作约束时要求任意构造函数签名,即。哪里T : new(string),或哪里T : new(string, int)
  5. 我也同意这里关于修复事件的另一张海报,包括空事件列表和并发设置(尽管后者很棘手),
  6. 运算符应该定义为扩展方法,而不是类的静态方法(或者至少不只是静态方法),
  7. 允许接口的静态属性和方法(Java 有这个,但 C# 没有),
  8. 允许在对象初始值设定项中进行事件初始化(目前仅允许字段和属性),
  9. 为什么“对象初始值设定项”语法仅在创建对象时可用?为什么不随时提供它,即。var e = new Foo(); e { Bar = baz };
  10. 修复二次可枚举行为
  11. 所有集合都应该具有用于​​迭代的不可变快照(即,改变集合不应使迭代器失效),
  12. 元组很容易添加,但是像“ Either<T>”这样的有效封闭代数类型不是,所以我喜欢某种方式来声明封闭代数类型并对其实施详尽的模式匹配(基本上是对访问者模式的一流支持,但是效率更高);所以只需要枚举,用详尽的模式匹配支持扩展它们,并且不允许无效的情况,
  13. 我喜欢支持模式匹配,但至少支持对象类型测试;我也有点喜欢这里另一篇文章中提出的 switch 语法,
  14. 我同意另一篇文章,即System.IO类,例如Stream,设计得有些糟糕;任何需要抛出一些实现的接口NotSupportedException都是一个糟糕的设计,
  15. IList应该比现在简单得多;事实上,这可能适用于许多具体的集合接口,例如ICollection
  16. 太多方法抛出异常,例如 IDictionary,
  17. 我更喜欢一种比 Java 中可用的更好的检查异常形式(请参阅有关如何完成此操作的类型和效果系统的研究),
  18. 修复泛型方法重载解析中的各种烦人的极端情况;例如,尝试提供两种重载的扩展方法,一种对引用类型进行操作,另一种对可为空的结构类型进行操作,看看您的类型推断是否喜欢这样,
  19. 提供一种安全地反映接口的字段和成员名称的方法,例如INotifyPropertyChanged,将字段名称作为字符串;您可以通过使用带有 lambda 的扩展方法来做到这一点MemberExpression,即。() => Foo,但这不是很有效,
    • 更新:C# 6.0nameof()为单个成员名称添加了运算符,但它在泛型中不起作用(nameof(T) == "T"而不是实际类型参数的名称:您仍然需要这样做typeof(T).Name)) - 也不允许您获得“路径”字符串,例如nameof(this.ComplexProperty.Value) == "Value"限制其可能的应用。
  20. 允许接口中的运算符,并使所有核心数字类型都实现IArithmetic;其他有用的共享操作员界面也是可能的,
  21. 使改变对象字段/属性变得更加困难,或者至少,允许注释不可变字段并使类型检查器强制执行它(只需将其视为仅限 getter 的属性 fer chrissakes,这并不难!);事实上,以更明智的方式统一字段和属性,因为两者兼而有之是没有意义的;C# 3.0 的自动属性是朝这个方向迈出的第一步,但还远远不够,
    • 更新:虽然 C# 有readonly关键字,并且 C# 6.0 添加了只读自动属性,但它不像真正的语言对不可变类型和值的支持那么严格。
  22. 简化声明构造函数;我喜欢 F# 的方法,但这里的另一篇只需要“new”而不是类名的帖子至少更好,

That's enough for now I suppose. These are all irritations I've run into in the past week. I could probably go on for hours if I really put my mind to it. C# 4.0 is already adding named, optional and default arguments, which I emphatically approve of.

我想这已经足够了。这些都是我在过去一周遇到的烦恼。如果我真的下定决心,我可能会坚持几个小时。C# 4.0 已经添加了命名、可选和默认参数,我非常赞同。

Now for one unreasonable request:

现在提出一个不合理的要求:

  1. it'd be really, reallynice if C#/CLR could support type constructor polymorphism, ie. generics over generics,
  1. 这会是真的,真的很不错,如果C#/ CLR可以支持类型构造多态性,即。泛型优于泛型,

Pretty please? :-)

请问漂亮吗?:-)

回答by ChrisW

Some people (ISVs) wish that you could compile it to machine code at build time, and link it, in order to create a native executable which doesn't need the dotNet run-time.

有些人 (ISV) 希望您可以在构建时将其编译为机器代码并链接它,以便创建不需要 dotNet 运行时的本机可执行文件。

回答by Marc Gravell

  • the Reset()method on IEnumerator<T>was a mistake (for iterator blocks, the language spec even demandsthat this throws an exception)
  • the reflection methods that return arrays were, in Eric's view, a mistake
  • array covariance was and remains an oddity
    • Update: C# 4.0 with .NET 4.0 added covariant/contravariance support to generic interfaces (like IEnumerable<out T>and Func<in T, out TResult>, but not concrete types (like List<T>).
  • ApplicationExceptionrather fell out of favor - was that a mistake?
  • synchronized collections - a nice idea, but not necessarily useful in reality: you usually need to synchronize multipleoperations (Contains, then Add), so a collection that synchronizes distinct operations isn't all that useful
    • Update: The System.Collections.Concurrenttypes, with TryAdd, GetOrAdd, TryRemove, etc were added in .NET Framework 4.0 - though methods that accept a factory delegate do not guarantee the factory will only be invoked once per key.
  • more use could have been made of the using/lockpattern - perhaps allowing them to share a re-usable (extensible?) syntax; you can simulate this by returning IDisposableand using using, but it could have been clearer
  • iterator blocks : no simple way of checking arguments ahead-of-time (rather than lazily). Sure, you can write two chained methods, but that is ugly
  • simpler immutability would be nice; C# 4.0 helps a bit, but not quite enough
  • no "this ref-type parameter cannot be null" support - although contracts (in 4.0) help with this somewhat. But syntax like Foo(SqlConnection! connection)(that injects a null-check / throw) would be nice (contrast to int?etc)
  • lack of support of operators and non-default constructors with generics; C# 4.0 solves this a bit with dynamic, or you can enable it like this
  • the iterator variable being declared outsidethe while in the foreachexpansion, meaning that anon-methods/lambdas capture the single variable, rather than one per iteration (painful with threading/async/etc)
  • Reset()on的方法IEnumerator<T>是错误的(对于迭代器块,语言规范甚至要求抛出异常)
  • 在 Eric 看来,返回数组的反射方法是错误的
  • 数组协方差曾经并且仍然是一个奇怪的现象
    • 更新:带有 .NET 4.0 的 C# 4.0 添加了对泛型接口(如IEnumerable<out T>Func<in T, out TResult>,但不是具体类型(如List<T>))的协变/逆变支持。
  • ApplicationException而是失宠了——这是一个错误吗?
  • 同步集合 - 一个不错的主意,但在现实中不一定有用:您通常需要同步多个操作(Contains, then Add),因此同步不同操作的集合并不是那么有用
  • 可以更多地使用using/lock模式 - 也许允许它们共享可重用(可扩展?)语法;您可以通过返回IDisposable和使用来模拟这一点using,但它本来可以更清楚
  • 迭代器块:没有提前(而不是懒惰)检查参数的简单方法。当然,您可以编写两个链式方法,但这很丑陋
  • 更简单的不变性会很好;C# 4.0有点帮助,但还不够
  • 没有“此 ref 类型参数不能为空”支持 - 尽管合同(在 4.0 中)对此有所帮助。但是像Foo(SqlConnection! connection)(注入空检查 / throw)这样的语法会很好(与int?等相反)
  • 缺乏对带有泛型的运算符和非默认构造函数的支持;C# 4.0 解决了这个问题dynamic,或者你可以像这样启用它
  • 迭代器变量在扩展中的 while之外声明foreach,这意味着匿名方法/lambdas 捕获单个变量,而不是每次迭代一个(对线程/异步/等很痛苦)

回答by Dave Markle

The .Parameters.Add() method on the SqlCommand in V1 of the framework was horribly designed -- one of the overloads would basically not work if you passed in a parameter with a value (int) of 0 -- this led to them creating the .Parameters.AddWithValue() method on the SqlCommand class.

框架 V1 中 SqlCommand 上的 .Parameters.Add() 方法设计得很糟糕——如果你传入一个值 (int) 为 0 的参数,其中一个重载基本上不起作用——这导致他们创建SqlCommand 类上的 .Parameters.AddWithValue() 方法。

回答by Ray Lu

  • Be able to invoke an extension method on null variable is arguable e.g.

    object a=null; a.MyExtMethod(); // this is callable, assume somewhere it has defined MyExtMethod

    It could be handy but it is ambiguous on null reference exception topics.

  • One naming 'flaw'. 'C' of "configuration" in System.configuration.dll should be capitalized.

  • Exception handling. Exception should be forcibly caught or thrown like in Java, the compiler should check it at compilation time. Users should not rely on comments for exceptions info within the target invocation.

  • 能够在空变量上调用扩展方法是有争议的,例如

    对象 a=null; a.MyExtMethod(); // 这是可调用的,假设它在某处定义了 MyExtMethod

    它可能很方便,但在空引用异常主题上是模棱两可的。

  • 一种命名为“缺陷”。System.configuration.dll 中“配置”的“C”应该大写。

  • 异常处理。异常应该像 Java 一样被强制捕获或抛出,编译器应该在编译时检查它。用户不应依赖于目标调用中异常信息的注释。

回答by lomaxx

Implicitly Typed variables were implemented poorly IMO. I know you should really only use them when working with Linq expressions, but it's annoying that you can't declare them outside of local scope.

隐式类型变量在 IMO 中的实现很差。我知道你真的应该只在使用 Linq 表达式时使用它们,但是你不能在本地范围之外声明它们很烦人。

From MSDN:

来自 MSDN:

  • var can only be used when a local variable is declared and initialized in the same statement; the variable cannot be initialized to null, or to a method group or an anonymous function.
  • var cannot be used on fields at class scope.
  • Variables declared by using var cannot be used in the initialization expression. In other words, this expression is legal: int i = (i = 20); but this expression produces a compile-time error: var i = (i = 20);
  • Multiple implicitly-typed variables cannot be initialized in the same statement.
  • If a type named var is in scope, then the var keyword will resolve to that type name and will not be treated as part of an implicitly typed local variable declaration.
  • var 只能在同一个语句中声明和初始化局部变量时使用;该变量不能初始化为 null,也不能初始化为方法组或匿名函数。
  • var 不能用于类范围内的字段。
  • 使用 var 声明的变量不能在初始化表达式中使用。换句话说,这个表达式是合法的:int i = (i = 20); 但是这个表达式会产生一个编译时错误:var i = (i = 20);
  • 不能在同一个语句中初始化多个隐式类型变量。
  • 如果名为 var 的类型在范围内,则 var 关键字将解析为该类型名称,并且不会被视为隐式类型局部变量声明的一部分。

The reason I think it's a poor implementation is that they call it var, but it's a long way from being a variant. It's really just shorthand syntax for not having to type the fully class name (except when used with Linq)

我认为这是一个糟糕的实现的原因是他们称它为 var,但它离变体还有很长的路要走。这实际上只是不必键入完整类名的速记语法(与 Linq 一起使用时除外)

回答by BenAlabaster

I don't know that I'd go as far as to say it's a design flaw, but it would be really nice if you could infer a lambda expression in the same way you can in VB:

我不知道我会说这是一个设计缺陷,但是如果您能像在 VB 中一样推断 lambda 表达式,那就太好了:

VB:

VB:

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

C#

C#

It would be nice if could do this:

如果可以这样做就好了:

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

Instead of having to do this:

而不是必须这样做:

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

I realise it's not much longer, but in Code Golf every character counts damnit! Don't they take that into account when they design these programming languages? :)

我意识到它不会更长,但在 Code Golf 中,每个角色都该死!他们在设计这些编程语言时没有考虑到这一点吗?:)

回答by tuinstoel

I don't understand that you can't do

我不明白你做不到

where T : new(U)

其中 T : 新 (U)

So you declare that generic type T has a non-default constructor.

所以你声明泛型类型 T 有一个非默认构造函数。

edit:

编辑:

I want to do this:

我想做这个:

public class A 
{
    public A(string text) 
    {

    }
}


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

}

回答by JoshBerke

  1. I'm not a big fan of the Stream, StringWriter, StringReader, TextReader, TextWriter classes...it's just not intuitive what is what.
  2. IEnumerable.Reset throwing an exception for iterators. I have some third party components which always call reset when databound, requires me to cast to a list first to use these.
  3. Xml Serializer should have serialized IDictionary elements
  4. I totally forgot about the HttpWebRequest & FTP API what a pain in my....(thanks for the comment Nicholas to remind me of this:-)
  1. 我不是 Stream、StringWriter、StringReader、TextReader、TextWriter 类的忠实粉丝……它只是不直观。
  2. IEnumerable.Reset 为迭代器抛出异常。我有一些第三方组件在数据绑定时总是调用重置,需要我首先转换到列表才能使用这些。
  3. Xml Serializer 应该有序列化的 IDictionary 元素
  4. 我完全忘记了 HttpWebRequest 和 FTP API,这对我来说是多么痛苦......(感谢 Nicholas 的评论提醒我这一点:-)

Edit
5. Another annoyance of mine is how System.Reflection.BindingFlags, has different uses depending on the method your using. In FindFields for example what does CreateInstance or SetField mean? This is a case where they have overloaded the meaning behind this enumeration which is confusing.

编辑
5. 我的另一个烦恼是 System.Reflection.BindingFlags 如何根据您使用的方法有不同的用途。例如在 FindFields 中 CreateInstance 或 SetField 是什么意思?在这种情况下,他们重载了这个枚举背后的含义,这令人困惑。

回答by Scott Weinstein

A small C# pet peev - constructors use the C++/Java syntax of having the constructor be the same name as the class.

一个小的 C# 宠物 peev - 构造函数使用 C++/Java 语法,使构造函数与类同名。

New()or ctor()would have been much nicer.

New()或者ctor()会更好。

And sure, tools such as coderush make this less of an issue for renaming classes, but from a readability POV, New() provides great clarity.

当然,coderush 之类的工具使重命名类的问题不那么严重,但从可读性 POV 来看,New() 提供了非常清晰的信息。

回答by ggf31416

Some classes implement interfaces but they don't implement many of the methods of that interface, for example Array implements IList but 4 out of 9 methods throw NotSupportedException http://msdn.microsoft.com/en-us/library/system.array_members.aspx

有些类实现了接口,但它们没有实现该接口的许多方法,例如 Array 实现了 IList 但 9 个方法中有 4 个抛出 NotSupportedException http://msdn.microsoft.com/en-us/library/system.array_members .aspx