C#(.NET)devise瑕疵

C#或.NET Framework中最大的devise缺陷是什么?

例如:没有不可为空的stringtypes,并且从IDataReader获取值时必须检查DBNull。

我同意这篇文章 (对于那些缺乏ToString的人来说,有一个debugging器属性可以为你的类提供一个自定义的格式)。

在上面的列表之上,我还会添加以下合理的请求:

  1. 不可为空的引用types作为对可空值types的补充,
  2. 允许覆盖一个结构的空构造函数,
  3. 允许genericstypes约束来指定密封类,
  4. 我同意这里另一个海报,要求任意构造函数签名时使用作为约束,即。 其中T:new(string),或者其中T:new(string,int),
  5. 我也同意这里的另一个海报修复事件,无论是空的事件列表和并发设置(虽然后者是棘手的),
  6. 运算符应该被定义为扩展方法,而不是作为类的静态方法(或者至less不是静态方法),
  7. 允许接口的静态属性和方法(Java有这个,但C#没有),
  8. 允许对象初始化器中的事件初始化(只允许字段和属性),
  9. 为什么“对象初始值设定项”语法仅在创build对象时可用? 为什么不能在任何时候提供,即。 var e = new Foo(); e {Bar = baz};
  10. 修复二次可枚举的行为,
  11. 所有的集合都应该有迭代的不可改变的快照(即,改变集合不应该使迭代器无效),
  12. 元组很容易添加,但是像“Either”这样的高效的闭代数types不是,所以我想用某种方式来声明一个封闭的代数types,并在其上强制执行详尽的模式匹配(基本上对访问者模式的一stream支持,但效率更高); 所以只要拿枚举,用详尽的模式匹配支持来扩展它们,不允许有无效的情况,
  13. 我一般喜欢支持模式匹配,但至less对于对象typestesting来说, 我也有点像另一篇文章中提出的开关语法,
  14. 我同意另一篇文章,像Stream一样,System.IO类的devise有点糟糕; 任何需要一些实现来引发NotSupportException的接口是一个糟糕的devise,
  15. IList应该比现在简单得多。 实际上,对于许多具体的集合接口,这可能是正确的,比如ICollection,
  16. 太多的方法抛出exception,例如像IDictionary,
  17. 我更喜欢比Java中可用的检查exception更好的forms(参见types和效果系统的研究)
  18. 在generics方法重载parsing中修复各种烦人的angular落案例; 例如,尝试提供两个重载的扩展方法,一个对引用types进行操作,另一个对可空的结构types进行操作,并查看types推断是如何使用的,
  19. 提供一种方法来安全地反映INotifyPropertyChanged等接口的字段和成员名称,这些接口将字段名称视为string; 你可以通过使用带有MemberExpression的lambdaexpression式的扩展方法来做到这一点。 ()=> Foo,但这不是很有效率,
  20. 允许接口中的运算符,并使所有核心数字types实现IAalgorithm; 其他有用的共享操作员界面也是可能的,
  21. 使对象字段/属性变得更难,或者至less允许注释不可变字段,并使types检查器强制执行它(只要把它当作只有getter的属性,就不难了! 事实上,以更合理的方式统一领域和属性,因为两者都没有意义。 C#3.0的自动属性是朝这个方向迈出的第一步,但是它们不够远,
  22. 简化构造函数的声明; 我喜欢F#的方法,但是另一个需要简单的“新”而不是类名的post至less是更好的,

我想现在已经足够了。 这些都是我过去一周遇到的一切烦恼。 如果我真的把自己的想法付诸实践,我可能会继续工作数小时。 C#4.0已经添加了命名的,可选的和默认的参数,我强烈赞同。

现在有一个不合理的要求:

  1. 如果C#/ CLR可以支持types构造函数多态性,那真是非常好。 generics的generics,

请好吗? 🙂

  • IEnumerator<T>上的Reset()方法是一个错误(对于迭代器块,语言规范甚至要求抛出exception)
  • 埃里克认为,返回数组的reflection方法是一个错误
  • arrays协方差是和仍然是一个古怪的; 至less在C#4.0 / .NET 4.0中, IEnumerable[<T>]
  • ApplicationException相当失宠 – 是一个错误?
  • 同步集合 – 一个好主意,但在现实中并不一定有用:通常需要同步多个操作( Contains ,然后Add ),所以同步不同操作的集合并不是那么有用
  • 可能会使用更多的using / lock模式 – 也许允许它们共享可重用(可扩展的)语法; 你可以通过返回IDisposable和using using模拟这个,但是它可以更清晰
  • 迭代器块:没有简单的方法提前检查参数(而不是懒惰地)。 当然,你可以写两个链接的方法,但这是丑陋的
  • 更简单的不变性将是很好的; C#4.0 有一点帮助,但还不够
  • 没有“这个ref-type参数不能为空”的支持 – 虽然合同(在4.0)有所帮助。 但像Foo(SqlConnection! connection) (注入一个空检查/ throw )的语法将是很好(对比int?等)
  • 缺乏运营商和非默认的generics构造者的支持; C#4.0解决了这一点dynamic ,或者你可以这样启用它

  • 迭代器variables在foreach扩展之外被声明,这意味着anon-methods / lambdas捕获单个variables,而不是每次迭代一次(使用线程/asynchronous/等痛苦)

TextWriter是StreamWriter的类。 跆拳道?

这总让我迷惑到极点。

一个小的C#pet peev构造函数使用C ++ / Java语法来让构造函数与类相同。

New()ctor()会好得多。

当然Coderush这样的工具可以减less重命名类的问题,但是从可读性POV中,New()提供了很好的清晰度。

我不明白你做不到

T:new(U)

所以你声明genericstypesT有一个非默认的构造函数。

编辑:

我想做这个:

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

我真的很惊讶,我是第一个提到这个:

ADO.NETtypes的数据集不会将可空列暴露为可为空types的属性。 你应该可以写这个:

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

相反,你必须写这个,这是愚蠢的:

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

这在.NET 2.0中很烦人,现在更加烦人的是你必须在你漂亮整洁的LINQ查询中使用像上面那样的jiggery-pokery。

同样令人讨厌的是,生成的Add<TableName>Row方法与可空types的概念类似。 更重要的是,因为生成的TableAdapter方法不是。

.NET中没有太多东西让我觉得像开发团队说的那样:“好的,男孩们,我们已经够接近了!” 但是这个确实如此。

  1. 我不是Stream,StringWriter,StringReader,TextReader,TextWriter类的忠实粉丝……这只是不直观的是什么。
  2. IEnumerable.Reset为迭代器抛出一个exception。 我有一些第三方组件,总是在数据绑定时调用重置,需要我先投到列表使用这些。
  3. Xml序列化器应该有序列化的IDictionary元素
  4. 我完全忘了HttpWebRequest&FTP API是什么让我感到痛苦….(感谢评论尼古拉斯提醒我:-)

编辑
5.另一个我的烦恼是System.Reflection.BindingFlags,根据你使用的方法有不同的用途。 在FindFields中,例如CreateInstance或SetField是什么意思? 这是一个他们已经超载这个枚举背后的意思,这是混淆的。

我不知道我会尽可能地说这是一个devise缺陷,但是如果你可以像在VB中一样推断一个lambdaexpression式,这将是非常好的:

VB:

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

C#

这将是很好,如果可以做到这一点:

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

而不必这样做:

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

我意识到这不是更长的时间,但在代码高尔夫每个字符计数damnit! 他们在devise这些编程语言时不要考虑到这一点吗? 🙂

  1. System.Object类:

    • Equals和GetHashCode – 并不是所有的类都是可比的或可散列的,都应该移到一个接口上。 想到IEquatable或IComparable(或类似)。

    • ToString – 不是所有的类都可以被转换成一个string,应该被移动到一个接口。 想到IFormattable(或类似)。

  2. ICollection.SyncRoot属性:

    • 促进糟糕的devise,外部锁几乎总是更有用。
  3. generics应该从一开始就在那里:

    • System.Collections命名空间包含很多或多或less过时的类和接口。

其中一个令我感到不快的是Predicate<T> != Func<T, bool>悖论。 他们都是typesT -> bool代表,但他们不分配兼容。

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

我不喜欢C#switch语句。

我想要这样的东西

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

所以不要再有rest(容易忘记),并且可以用逗号分隔不同的值。

我们非常了解正确的面向对象技术。 解耦,按合同编程,避免不当inheritance,适当使用例外,开放/closures主体,Liskov可替代性等等。 不过,.Net框架并没有采用最佳实践。

对我来说,.Netdevise中最大的缺陷并不是站在巨人的肩膀上, 向使用框架的程序员群体推广不太理想的编程范例

如果MS注意到这一点,软件工程界在这个十年中可能会在质量,稳定性和可扩展性方面有很大的飞跃,但是,似乎正在倒退。

C#中的事件,您必须显式检查侦听器。 难道这不是事件的重点,要向谁在那里碰碰运气? 即使没有?

对于嵌套/recursion迭代器, O(N ^ 2)行为是可怕的(而且对大多数人来说是相当不可见的)。

我很内疚,他们知道这个问题,知道如何解决这个问题,但是没有被视为有足够的优先权值得包容。

我一直在用像树一样的树工作,当他们无意中以这种方式引入昂贵的操作时,必须纠正别的聪明人的代码。

“yield foreach”的优点在于,更简单,更简单的语法鼓励正确,高效的代码。这是我认为在为平台长期成功添加新function之前应该追求的“成功之坑” 。

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

接口中的静态成员和嵌套types。

当接口成员具有特定于接口的types的参数( 例如 enum )时,这是特别有用的。 将枚举types嵌套在接口types中将会很好。

事件的非常危险的默认性质。 由于用户被移除,您可以调用事件并处于不一致的状态,这一事实太可怕了。 请参阅Jon Skeet和Eric Lippert的优秀文章,了解更多关于这个主题的文章。

作为枚举的月光照

枚举的特殊性: 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

我的build议,把“@”标志好好利用:

代替:

如果((myVar&MyEnumName.ColorRed)!= 0)

用这个:

如果((myVar&MyEnumName.ColorRed)!= @ 0)

  • 无处不在。

  • 无处不在。

  • API是不一致的,例如,变异数组返回void但追加到StringBuffer返回相同的可变StringBuffer

  • 集合接口与不可变数据结构不兼容,例如Add in System.Collections.Generic.IList<_>无法返回结果。

  • 没有结构打字,所以你写的System.Windows.Media.Effects.SamplingMode.Bilinear而不是只是Bilinear

  • 当它应该是一个不可变的struct时,由类实现的可变IEnumerator接口。

  • 平等和比较是一团糟:你有System.IComparableEquals但是你也得到System.IComparable<_>System.IEquatableSystem.Collections.IComparerSystem.Collections.IStructuralComparableSystem.Collections.IStructuralEquatableSystem.Collections.Generic.IComparerSystem.Collections.Generic.IEqualityComparer

  • 元组应该是结构,但是结构不必要地抑制了尾部调用消除,所以最常见和最基本的数据types之一将不必要地分配并且销毁可扩展的并行性。

我们使用物业的方式有时会激怒我。 我喜欢把它们看作是Java的getFoo()和setFoo()方法的等价物。 但他们不是。

如果“ 属性使用指南”声明属性应该能够以任何顺序设置,以便序列化可以工作,那么它们对于设置时间validation是无用的。 如果您来自背景,希望阻止对象进入无效状态,则属性不是您的解决scheme。 有时候,我看不到公共会员比他们更好,因为我们在房地产方面该做什么样的事情是非常有限的。

为了达到这个目的,我一直都很希望(这里主要是大声思考,我只是希望我可以做这样的事情),我可以以某种方式扩展属性语法。 想象一下这样的事情:

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; } }
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(看ruby,看看我在说什么)解决问题,在mixins的主题。 将它们添加到语言中的一个非常好的方法是允许generics被用于inheritance。 这允许你以一种很好的面向对象的方式扩展现有的类:

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

这可以像这样使用来扩展一个string,例如:

 var newMixin = new MyMixin<string>(); 

它比扩展方法function强大得多,因为它允许你重写方法,比如把它们包装起来,允许在语言中使用类AOP的function。

对不起,咆哮:-)

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.

To add to the long list of good points made by others already:

  • DateTime.Now == DateTime.Now in most, but not all cases.

  • String which is immutable has a bunch of options for construction and manipulation, but StringBuilder (which is mutable) doesn't.

  • Monitor.Enter and Monitor.Exit should have been instance methods, so instead of newing a specific object for locking, you could new a Monitor and lock on that.

  • Destructors should never have been named destructors. The ECMA spec calls them finalizers, which is much less confusing for the C++ crowd, but the language specification still refers to them as destructors.

Microsoft won't fix obvious bugs in the framework and won't provide hooks so end users can fix them.

Also, there is no way to binary-patch .NET executables at runtime and no way to specify private versions of .NET framework libraries without binary patching the native libraries (to intercept the load call), and ILDASM is not redistributable so I cannot automate the patch anyway.

  • Be able to invoke an extension method on null variable is arguable eg

    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.

  1. There are no subsets of ICollection<T> and IList<T> ; at minimum, a covariant read-only collection interface IListSource<out T> (with an enumerator, indexer and Count) would have been extremely useful.
  2. .NET does not support weak delegates . The workarounds are clumsy at best, and listener-side workarounds are impossible in partial trust (ReflectionPermission is required).
  3. Generic interface unification is forbidden even when it makes sense and cause no problems.
  4. Unlike in C++, covariant return types are not allowed in .NET
  5. It is not possible to bitwise-compare two value types for equality. In a functional " persistent " data structure, I was writing a Transform(Sequence<T>, Func<T,T>) function that needed to quickly determine whether the function returns the same value or a different value. If the function does not modify most/all of its arguments, then the output sequence can share some/all memory from the input sequence. Without the ability to bitwise compare any value type T, a much slower comparison must be used, which hurts performance tremendously.
  6. .NET doesn't seem able to support ad-hoc interfaces (like those offered in Go or Rust) in a performant manner. Such interfaces would have allowed you to cast List<T> to a hypothetical IListSource<U> (where T:U) even though the class doesn't explicitly implement that interface. There are at least three different libraries (written independently) to supply this functionality (with performance drawbacks, of course–if a perfect workaround were possible, it wouldn't be fair to call it a flaw in .NET).
  7. Other performance issues: IEnumerator requires two interface calls per iteration. Plain method pointers (IntPtr-sized open delegates) or value-typed delegates (IntPtr*2) are not possible. Fixed-size arrays (of arbitrary type T) cannot be embedded inside classes. There is no WeakReference<T> (you can easily write your own, but it will use casts internally.)
  8. The fact that identical delegate types are considered incompatible (no implicit conversion) has been a nuisance for me on some occasions (eg Predicate<T> vs Func<T,bool> ). I often wish we could have structural typing for interfaces and delegates, to achieve looser coupling between components, because in .NET it is not enough for classes in independent DLLs to implement the same interface–they must also share a common reference to a third DLL that defines the interface.
  9. DBNull.Value exists even though null would have served the same purpose equally well.
  10. C# has no ??= operator; you must write variable = variable ?? value . Indeed, there are a few places in C# that needlessly lack symmetry. For example you can write if (x) y(); else z(); (without braces), but you can't write try y(); finally z();
  11. When creating a thread, it is impossible to cause the child thread to inherit thread-local values from the parent thread. Not only does the BCL not support this, but you can't implement it yourself unless you create all threads manually; even if there were a thread-creation event, .NET can't tell you the "parents" or "children" of a given thread.
  12. The fact that there are two different length attributes for different data types, "Length" and "Count", is a minor nuisance.
  13. I could go on and on forever about the poor design of WPF … and WCF (though quite useful for some scenarios) is also full of warts. In general, the bloatedness, unintuitiveness, and limited documentation of many of the BCL's newer sublibraries makes me reluctant to use them. A lot of the new stuff could have been far simpler, smaller, easier to use and understand, more loosely coupled, better-documented, applicable to more use cases, faster, and/or more strongly typed.
  14. I'm often been bitten by the needless coupling between property getters and setters: In a derived class or derived interface, you can't simply add a setter when the base class or base interface only has a getter; if you override a getter then you are not allowed to define a setter; and you can't define the setter as virtual but the getter as non-virtual.

One thing that ticked me off in 1.x was when using the System.Xml.XmlValidatingReader , the ValidationEventHandler 's ValidationEventArgs doesn't expose the underlying XmlSchemaException (marked internal) which has all the useful info like linenumber and position . Instead you're expected to parse this out of the Message string property or use reflection to dig it out. Not so good when you want to return a more sanitised error to the end user.

Don't like it that you can't use the values of one enum in another enum, for example:

  enum Colors { white, blue, green, red, black, yellow } enum SpecialColors { Colors.blue, Colors.red, Colors.Yellow } 

I honestly have to say that during my years of .NET ( C# ) programming I haven't flaws in the framework design that I've remembered; Meaning that in my case there are probably no flaws that are worth remembering.

However, there is something that I dissliked a couple of years back when Microsoft was releasing XNA, they completely cut of their MDX 2.0-version, which made my games unplayable and not easy to just convert. This is a broader flaw and has nothing to do with the .NET-framework.

The .NET-framework actually follows a lot of Very Good design guidelines developed by a lot of the high end language architectures. So I have to say that im happy about .NET.

But to tell you something that could be better, I'd have to complain about the Generic system, I don't find the Generics for Interfaces such as "where T is MyObj" ( that's not the completely correct syntax. However, this part could have been made much better and clearer.

Imagine having an Interface which 2 different classes are sharing, if you want a Generic method inside that interface, you need to go over some nasty Generics-sytanx. It might just be me wanting to do weird stuff. Only memmorable thing for me though.