一般而言,C#或.NET Framework中最大的设计缺陷是什么?
示例:没有不可为空的字符串类型,从IDataReader提取值时必须检查DBNull。
一般而言,C#或.NET Framework中最大的设计缺陷是什么?
示例:没有不可为空的字符串类型,从IDataReader提取值时必须检查DBNull。
Answers:
我对此帖子表示强烈同意(对于那些缺乏ToString的人来说,有一个debugger属性可以为您的类提供自定义格式)。
在以上列表的顶部,我还将添加以下合理要求:
T : new(string)哪里T : new(string, int)var e = new Foo(); e { Bar = baz };Either<T>”这样的高效封闭式代数类型却不是,因此我很喜欢一种方法来声明封闭式代数类型并对其进行强制性模式匹配(基本上是对访问者模式的一流支持,但是效率更高);因此,只需使用枚举,并通过详尽的模式匹配支持对其进行扩展,并且不允许出现无效的情况,System.IO像这样的类Stream在设计上有些差;需要一些实现的任何接口NotSupportedException都是不好的设计,IList应该比它简单得多;实际上,对于许多具体的收集界面(例如ICollection,INotifyPropertyChanged将字段名作为字符串;您可以使用扩展方法,该方法采用带有的lambda MemberExpression,即。() => Foo,但这不是很有效,
nameof()用于单个成员名称的运算符,但是它不适用于泛型(nameof(T) == "T"而不是实际的类型参数名称:您仍然需要这样做typeof(T).Name))-也不允许您获取“路径”字符串,例如nameof(this.ComplexProperty.Value) == "Value"限制其可能的应用。IArithmetic;其他有用的共享操作员界面也是可能的,readonly关键字,并且C#6.0添加了只读自动属性,但是它不像对不可变类型和值的真实语言支持那样严格。我想现在就足够了。这些都是我过去一周遇到的烦恼。如果我真的下定决心,我可能会继续工作几个小时。C#4.0已经添加了命名,可选和默认参数,我对此表示赞同。
现在,对于一个不合理的请求:
漂亮吗?:-)
List<T>有100万个Ts。您如何建议有效地拍摄快照?#21:使用readonly关键字...。虽然这里有一些不错的建议,但它们大多只是建议-而非设计缺陷。
Reset()方法IEnumerator<T>是错误的(对于迭代器块,语言规范甚至要求这引发异常)IEnumerable<out T>和Func<in T, out TResult>,但没有具体类型(如List<T>))添加了协变/逆方差支持。ApplicationException 而是失宠了-这是一个错误吗?Contains,然后是Add),因此同步不同操作的集合并没有那么有用
System.Collections.Concurrent类型的,有TryAdd,GetOrAdd,TryRemove,等在.NET Framework 4.0中增加-尽管接受工厂委托方法不能保证工厂将只调用每个键一次做。using/lock模式可能会得到更多使用-也许允许它们共享可重用(可扩展的)语法;您可以通过返回IDisposable并使用using,但可能更清楚了Foo(SqlConnection! connection)(注入null-check / throw)这样的语法会很好(与int?etc对比)
dynamic,也可以像这样启用它foreach,这意味着匿名方法/ lambda捕获单个变量,而不是每次迭代捕获一个变量(线程/异步/等痛苦)
ApplicationException是一个错误-没像他们希望的那样有用。他们还说System.Exception应该这样abstract。
TextWriter是StreamWriter的基类。wtf?
这总是使我感到困惑。
C#小技巧-构造函数使用C ++ / Java语法,使构造函数与类同名。
New()否则ctor()会更好。
可以肯定的是,诸如Coderush之类的工具使得重命名类的问题减少了,但是从可读性的角度来看,New()提供了极大的清晰度。
class Foo { new(int j) {i = j} int i; }
New--uppercase关键字违反常规),但我还是毫不犹豫地称其为设计缺陷。他们想吸引现有的C ++ / Java开发人员,并且借用许多愚蠢的旧语法约定可以说帮助他们实现了目标。
我不明白你做不到
其中T:new(U)
因此,您声明泛型T具有非默认构造函数。
编辑:
我想做这个:
public class A
{
public A(string text)
{
}
}
public class Gen<T> where T : new(string text)
{
}
我是第一个提到这个的人,我真的很惊讶:
ADO.NET类型的数据集不会将可空列公开为可空类型的属性。您应该可以这样写:
int? i = myRec.Field;
myRec.Field = null;
相反,您必须编写以下代码,这很愚蠢:
int? i = (int?)myRec.IsFieldNull() ? (int?)null : myRec.Field;
myRec.SetFieldNull();
在.NET 2.0中,这很烦人,现在更烦人的是,您必须在漂亮的LINQ查询中像上面那样使用jiggery-pokery。
同样令人烦恼的是,生成的Add<TableName>Row方法对于可空类型的概念同样不明智。更重要的是,因为生成的TableAdapter方法不是。
.NET中没有太多内容使我感到开发团队说:“好吧,男孩们,我们已经足够亲密-运送它!” 但这确实可以。
DBNull.Value,因为null它本身就足以表示NULL。幸运的是,LINQ-to-SQL仅将null用作NULL。
编辑
5.我的另一个烦恼是System.Reflection.BindingFlags如何根据您使用的方法具有不同的用途。例如在FindFields中,CreateInstance或SetField是什么意思?在这种情况下,他们已经使此枚举背后的含义过载了,这很令人困惑。
我不知道我会说这是一个设计缺陷,但是如果您可以像在VB中一样推导lambda表达式,那将非常好:
VB:
Dim a = Function(x) x * (x - 1)
C#
如果可以这样做会很好:
var a = x => x * (x - 1);
不必这样做:
Func<int, int> a = x => x * (x - 1);
我意识到这已经不多了,但是在Code Golf中,每个角色都很重要!他们在设计这些编程语言时没有考虑到这一点吗?:)
(int x) => x * (x -1);可能意味着Func<int, int>或可能意味着Expression<Func<int, int>>
+没有为定义object。
Equals和GetHashCode-并非所有类都是可比较的或可哈希的,应将其移至接口。想到IEquatable或IComparable(或类似版本)。
ToString-并非所有类都可以转换为字符串,应将其移至接口。我想到了IFormattable(或类似格式)。
泛型应该从一开始就存在:
EqualityComparer<T>.Default正确更新即可。然后两者var dict = new Dictionary<object, string>(EqualityComparer<object>.Default)和var dict = new Dictionary<object, string>()将使用参考比较/平等。
EqualityComparer<T>.Default它的作用。无需检查每个查找。比较器是Dictionary实例的一个属性,每个实例都Dictionary知道它在使用哪个实例。
令我烦恼的是Predicate<T> != Func<T, bool>悖论。他们都是类型的委托,T -> bool但它们与分配不兼容。
某些人(ISV)希望您可以在构建时将其编译为机器代码并进行链接,以创建不需要dotNet运行时的本机可执行文件。
我们对正确的OO技术了解很多。解耦,按合同编程,避免不正确的继承,异常的适当使用,打开/关闭的主体,Liskov可替换性等等。到目前为止,.Net框架还没有采用最佳实践。
对我来说,.Net设计中最大的缺陷不是站在巨人的肩膀上;而是在互联网上。向使用框架的程序员推广不太理想的编程范例。
如果MS对此予以关注,那么在这十年中,软件工程界可能在质量,稳定性和可伸缩性方面取得了巨大飞跃,但是可惜,它似乎正在倒退。
我不喜欢C#switch语句。
我想要这样的东西
switch (a) {
1 : do_something;
2 : do_something_else;
3,4 : do_something_different;
else : do_something_weird;
}
因此,不再有中断(容易忘记)的可能性,并且可以用逗号分隔不同的值。
switch从根本上来说,所有模仿C的残废版本(针对速度进行了优化!)的语言都被破坏了。VB的性能要好得多,但与模式匹配的语言(Haskell,F#…)相比仍然落后了数年。
C#中的事件,您必须在其中显式检查侦听器。广播事件的重点不是向碰巧在那里的任何人广播吗?即使没有?
一些类实现接口,但它们未实现该接口的许多方法,例如Array实现IList,但9种方法中有4种抛出NotSupportedException http://msdn.microsoft.com/zh-cn/library/system.array_members .aspx
null 到处。
const 无处。
API是不一致的,例如,对数组return进行变异,void但在return后面追加StringBuffer相同的mutable 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。
元组应该是结构,但是结构不必要地抑制了尾部调用消除,因此最常见和基本的数据类型之一将不必要地分配并破坏可伸缩并行性。
IEnumerator?
0月光作为枚举
枚举的特性: http //blogs.msdn.com/abhinaba/archive/2007/01/09/more-peculiarites-of-enum.aspx
我的建议,请充分利用“ @”符号:
代替:
如果((myVar&MyEnumName.ColorRed)!= 0)
用这个:
如果((myVar&MyEnumName.ColorRed)!= @ 0)
要添加一长串已经由他人提出的优点:
DateTime.Now == DateTime.Now 在大多数情况下(并非所有情况下)。
String这是不可变的,具有一堆构造和操作选项,但StringBuilder(可变)没有。
Monitor.Enter并且Monitor.Exit应该是实例方法,因此您可以新建一个aMonitor并对其进行锁定,而不是为锁定而新建一个特定的对象。
绝对不应将析构函数命名为析构函数。ECMA规范称它们为终结器,对于C ++人群来说,混淆器要少得多,但是语言规范仍将它们称为析构函数。
DateTime.Now一个是世界上最明显的竞争条件,但为+1,其余
我们使用属性的方式有时让我感到恼火。我喜欢将它们视为Java的getFoo()和setFoo()方法的等效项。但事实并非如此。
如果《属性使用指南》指出应该可以以任何顺序设置属性,以便可以进行序列化,那么它们对于设置时间验证是无用的。如果您来自一个想防止对象自身进入无效状态的背景,那么属性不是您的解决方案。有时我看不到他们比公众成员更好,因为我们在应该考虑的事情上受限制在房地产领域做。
为此,我一直都希望(可以在这里大声思考,只是希望可以做这样的事情)我可以某种方式扩展属性语法。想象这样的事情:
private string password;
public string Password
{
// Called when being set by a deserializer or a persistence
// framework
deserialize
{
// I could put some backward-compat hacks in here. Like
// weak passwords are grandfathered in without blowing up
this.password = value;
}
get
{
if (Thread.CurrentPrincipal.IsInRole("Administrator"))
{
return this.password;
}
else
{
throw new PermissionException();
}
}
set
{
if (MeetsPasswordRequirements(value))
{
throw new BlahException();
}
this.password = value;
}
serialize
{
return this.password;
}
}
我不确定这是否有用,或者访问这些东西会是什么样子。但是我只是希望我可以对属性做更多的事情,并像对待get和set方法一样对待它们。
扩展方法虽然不错,但是对于解决本来可以用真正的mixins解决的问题而言,这是一种丑陋的方式(请参阅ruby以了解我在说什么)。将它们添加到语言中的一种非常不错的方法是允许将泛型用于继承。这允许您以一种很好的面向对象的方式扩展现有的类:
public class MyMixin<T> : T
{
// etc...
}
可以像这样使用它来扩展字符串,例如:
var newMixin = new MyMixin<string>();
它比扩展方法功能强大得多,因为它允许您重写方法,例如将它们包装起来,从而在语言内部提供类似于AOP的功能。
对不起,咆哮:-)
Microsoft不会修复框架中的明显错误,也不会提供挂钩,以便最终用户可以修复它们。
另外,在运行时无法对.NET可执行文件进行二进制修补,也无法在不对本机库进行二进制修补(以拦截加载调用)的情况下指定.NET Framework库的私有版本,并且ILDASM无法重新分发,因此我无法实现自动化反正补丁。
能否在null变量上调用扩展方法是有争议的,例如
对象a = null; a.MyExtMethod(); //这是可以调用的,假设它已经定义了MyExtMethod
它可能很方便,但是在空引用异常主题上模棱两可。
一种命名为“缺陷”。System.configuration.dll中“配置”的“ C”应大写。
异常处理。应该像Java中那样强制捕获或引发异常,编译器应在编译时对其进行检查。用户不应依赖注释来获取目标调用中的异常信息。
框架V1中SqlCommand上的.Parameters.Add()方法是经过可怕设计的-如果您传递的值(int)为0,则重载之一基本上将不起作用-这导致它们创建SqlCommand类上的.Parameters.AddWithValue()方法。
ICollection<T>和没有子集IList<T>;至少,协变只读集合接口IListSource<out T>(带有枚举器,索引器和Count)将非常有用。Transform(Sequence<T>, Func<T,T>)功能,功能需要快速确定该功能返回的是相同值还是不同值。如果函数未修改其大部分/所有参数,则输出序列可以共享输入序列中的部分/全部内存。如果无法按位比较任何值类型T,则必须使用慢得多的比较,这会严重损害性能。List<T>转换为假设IListSource<U>(T:U)。至少有三个 不同的 库(独立编写)来提供此功能(当然,在性能方面存在缺陷-如果可能有一个完美的解决方法,则称它为.NET缺陷是不公平的)。WeakReference<T>(您可以轻松编写自己的脚本,但是它将在内部使用强制类型转换。)Predicate<T>vs Func<T,bool>),相同的委托人类型被认为是不兼容的(没有隐式转换),这对我来说是个麻烦。我经常希望我们可以进行结构化打字对接口和委托,以实现组件之间的松散耦合,因为在.NET中,独立DLL中的类不足以实现相同的接口-它们还必须共享对第三个对象的公共引用定义接口的DLL。DBNull.Value即使null本来可以很好地达到相同的目的,也存在。variable = variable ?? value。实际上,C#中有一些地方不必要地缺乏对称性。例如,您可以书写if (x) y(); else z();(不带花括号),但不能书写try y(); finally z();。IList<T>,尽管我会使用IReadableByIndex<out T>和IAppendable<in T>。我也同意您的许多其他事情。
IListReader<T>;)上妥协-我将“源”一词用作“接收器”(仅写接口)的反义词。
IListSource<in T>还是IReadableList<out T>。使基本接口类型包含并非在所有派生中都存在的方法可能会有价值,尽管我认为使接口有些专门化通常会很好。例如,可能有一个IList<T>,其中包含可能会或可能不会起作用的调整大小的方法,以及一个IResizableList<T>会实现相同方法但保证它们应该起作用的方法。在字段可能包含对可变列表的唯一现存引用或对不可变列表的共享引用的情况下,这种方法可能很有用。
if (rl:(list as IResizableList<T>) != null) rl.Add(...);,但还有其他建议。作为各种集合和集合适配器的作者,让我感到烦恼的是编写了大量抛出异常的伪方法。作为类型安全迷,我不想被允许调用非法方法。一个IntelliSense风扇,我不想看到它们列出。
不喜欢不能在另一个枚举中使用一个枚举的值,例如:
enum Colors { white, blue, green, red, black, yellow }
enum SpecialColors { Colors.blue, Colors.red, Colors.Yellow }
typeof(Color)!= typeof(SpecialColors)。
enum SpecialColors { blue = Colors.blue, red = Colors.red, yellow = Colors.Yellow }
隐式类型变量在IMO中的实现效果很差。我知道您实际上只应该在使用Linq表达式时使用它们,但是令人讨厌的是,您不能在本地范围之外声明它们。
从MSDN:
我认为这是一个较差的实现,是因为他们将其称为var,但距离变体还有很长的路要走。它实际上只是不需要输入完整类名的简写语法(与Linq一起使用时除外)