What do you think are the biggest design flaws in C# or the .NET Framework in general?
My favorites are that there's no non-nullable string type and that you have to check for DBNull when fetching values from an IDataReader.
What do you think are the biggest design flaws in C# or the .NET Framework in general?
My favorites are that there's no non-nullable string type and that you have to check for DBNull when fetching values from an IDataReader.
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.
Reset()
method on IEnumerator<T>
was a mistake (for iterator blocks, the language spec even demands that this throws an exception)IEnumerable[<T>]
ApplicationException
rather fell out of favor - was that a mistake?Contains
, then Add
), so a collection that synchronizes distinct operations isn't all that usefulusing
/lock
pattern - perhaps allowing them to share a re-usable (extensible?) syntax; you can simulate this by returning IDisposable
and using using
, but it could have been clearerFoo(SqlConnection! connection)
(that injects a null-check / throw
) would be nice (contrast to int?
etc)dynamic
, or you can enable it like thisforeach
expansion, meaning that anon-methods/lambdas capture the single variable, rather than one per iteration (painful with threading/async/etc)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.
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.
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.
From MSDN:
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)
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:
Dim a = Function(x) x * (x - 1)
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? :)
I don't understand that you can't do
where T : new(U)
So you declare that generic type T has a non-default constructor.
edit:
I want to do this:
public class A
{
public A(string text)
{
}
}
public class Gen<T> where T : new(string text)
{
}
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.
A small C# pet peev - constructors use the C++/Java syntax of having the constructor be the same name as the class.
New()
or ctor()
would have been much nicer.
And sure, tools such as coderush make this less of an issue for renaming classes, but from a readability POV, New() provides great clarity.
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
Static members and nested types in interfaces.
This is particularly useful when an interface member has a parameter of a type that is specific to the interface (e.g. an enum
). It would be nice to nest the enum type in the interface type.
We know so much about the right OO techniques. Decoupling, programming by contract, avoiding improper inheritance, appropriate use of exceptions, open/closed principal, Liskov substitutability, and so on. Any yet, the .Net frameworks do not employ best practices.
To me the single biggest flaw in the design of .Net is not standing on the shoulders of giants; promoting less than ideal programming paradigms to the masses of programmers that use their frameworks.
If MS paid attention to this, the software engineering world could have made great leaps in terms of quality, stability and scalability in this decade, but alas, it seems to be regressing.
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.
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.
The way we use properties irritates me sometimes. I like to think of them as the equivalent of Java's getFoo() and setFoo() methods. But they are not.
If the Property Usage Guidelines.aspx) state that properties should be able to be set in any order so serialization can work, then they're useless for setter-time validation. If you come from a background where you like to prevent an object from allowing itself to ever get into an invalid state, then properties aren't your solution. Sometimes I fail to see just how they are better than public members, since we're so limited in what kinds of things we're supposed to do in properties.
To that end, I've always kind of wished (this is mostly thinking out loud here, I just kind of wish I could do something like this) that I could extend the property syntax somehow. Imagine something like this:
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;
}
}
I'm not sure if that's useful or what it accessing those would look like. But I just wish that I could do more with properties and really treat them like get and set methods.
0 moonlighting as enum
peculiarities of enum: http://blogs.msdn.com/abhinaba/archive/2007/01/09/more-peculiarites-of-enum.aspx
as illustrated by this good example: http://plus.kaist.ac.kr/~shoh/postgresql/Npgsql/apidocs/Npgsql.NpgsqlParameterCollection.Add_overload_3.html
my suggestion, put the "@" sign to good use:
instead of:
if ((myVar & MyEnumName.ColorRed) != 0)
use this:
if ((myVar & MyEnumName.ColorRed) != @0)
I'm really surprised that I'm the first to mention this one:
ADO.NET typed data sets don't expose nullable columns as properties of nullable types. You should be able to write this:
int? i = myRec.Field;
myRec.Field = null;
Instead, you have to write this, which is just stupid:
int? i = (int?)myRec.IsFieldNull() ? (int?)null : myRec.Field;
myRec.SetFieldNull();
This was annoying in .NET 2.0, and it's even more annoying now that you have to use jiggery-pokery like the above in your nice neat LINQ queries.
It's also annoying that the generated Add<TableName>Row
method is similarly insensible to the notion of nullable types. All the more so since the generated TableAdapter
methods aren't.
There's not a lot in .NET that makes me feel like the dev team said "Okay, boys, we're close enough - ship it!" But this sure does.
Extension methods are nice but they're an ugly way to solve problems that could have been solved cleaner with real mixins (look at ruby to see what I'm talking about), on the subject of mixins. A really nice way to add them to the language would have been to allow generics to be used for inheritance. This allows you to extend existing classes in a nice object oriented way:
public class MyMixin<T> : T
{
// etc...
}
this can be used like this to extend a string for example:
var newMixin = new MyMixin<string>();
It's far more powerful than extension methods because it allows you to override methods, for example to wrap them allowing AOP-like functionality inside the language.
Sorry for the rant :-)
The System.Object class:
Equals and GetHashCode - not all classes are comparable or hashable, should be moved to an interface. IEquatable or IComparable (or similar) comes to mind.
ToString - not all classes can be converted to a string, should be moved to an interface. IFormattable (or similar) comes to mind.
The ICollection.SyncRoot property:
Generics should have been there from the beginning:
TextWriter is a base class of StreamWriter. wtf?
That always confuses the hell out of me.
One of the things that irritates me is the Predicate<T> != Func<T, bool>
paradox. They're both delegates of type T -> bool
and yet they're not assignment compatible.
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.
The CLR (and therefore C#) doesn't support Multiple Inheritance and ASP.NET is stuffed with LSP breaks...
Those are my "favorites"...
I could probably find more bugs, but those are the ones I dislikes the most...!! :(
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.
I don't like the C# switch statement.
I would like something like this
switch (a) {
1 : do_something;
2 : do_something_else;
3,4 : do_something_different;
else : do_something_weird;
}
So no more breaks (easy to forget) and the possibility to comma-separate different values.
Events in C#, where you have to explicit check for listeners. Wasn't that the point with events, to broadcast to whoever happen to be there? Even if there aren't any?
The awful (and quite invisible to most people) O(N^2) behaviour of nested/recursive iterators.
I'm quite gutted that they know about it, know how to fix it but it is not viewed as having sufficient priority to merit inclusion.
I work with tree like structures all the time and have to correct otherwise smart people's code when they inadvertently introduce highly expensive operations in this way.
The beauty of "yield foreach' is that the simpler, easier syntax encourages correct, performant code. This is the "pit of success" that I think they should aspire to before adding new features for long term success of the platform.
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 }
The terribly dangerous default nature of events. The fact that you can call an event and be in an inconsistent state due to subscribers being removed is just horrible. See Jon Skeet's and Eric Lippert's excellent articles for more reading on the subject.
null
everywhere.
const
nowhere.
APIs are inconsistent, e.g. mutating an array returns void
but appending to a StringBuffer
returns the same mutable StringBuffer
.
Collection interfaces are incompatible with immutable data structures, e.g. Add
in System.Collections.Generic.IList<_>
cannot return a result.
No structural typing so you write System.Windows.Media.Effects.SamplingMode.Bilinear
instead of just Bilinear
.
Mutable IEnumerator
interface implemented by classes when it should be an immutable struct
.
Equality and comparison are a mess: you've got System.IComparable
and Equals
but then you've also got System.IComparable<_>
, System.IEquatable
, System.Collections.IComparer
, System.Collections.IStructuralComparable
, System.Collections.IStructuralEquatable
, System.Collections.Generic.IComparer
and System.Collections.Generic.IEqualityComparer
.
Tuples should be structs but structs unnecessarily inhibit tail call elimination so one of the most common and fundamental data types will allocate unnecessarily and destroy scalable parallelism.