Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# (.NET) Design Flaws [closed]

Tags:

c#

.net

  • the Reset() method on IEnumerator<T> was a mistake (for iterator blocks, the language spec even demands that 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>).
  • ApplicationException rather fell out of favor - was that a mistake?
  • synchronized collections - a nice idea, but not necessarily useful in reality: you usually need to synchronize multiple operations (Contains, then Add), so a collection that synchronizes distinct operations isn't all that useful
    • Update: The System.Collections.Concurrent types, 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/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 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)
    • Update: This is fixed in C# 8.0.
  • 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 outside the while in the foreach expansion, meaning that anon-methods/lambdas capture the single variable, rather than one per iteration (painful with threading/async/etc)
    • Update: This was fixed in C# 5.0.

TextWriter is a base class of StreamWriter. wtf?

That always confuses me to the extreme.


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.


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).

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.IO classes, like Stream, are somewhat poorly designed; any interface that requires some implementations to throw NotSupportedException is a bad design,
  15. IList should 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 readonly keyword, 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,

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.

Now for one unreasonable request:

  1. it'd be really, really nice if C#/CLR could support type constructor polymorphism, ie. generics over generics,

Pretty please? :-)


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) 
{

}