86

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

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

johnnyRose
  • 7,310
  • 17
  • 40
  • 61
Rauhotz
  • 7,914
  • 6
  • 40
  • 44

33 Answers33

73
  • 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)
  • 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)
Dai
  • 141,631
  • 28
  • 261
  • 374
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • IEnumerable != IEnumerable is indeed odd – Rauhotz Jan 04 '09 at 23:49
  • 2
    Well, the IEnumerable thing is a 1.1 hangover; you can use .Cast() with LINQ, at least – Marc Gravell Jan 04 '09 at 23:50
  • 8
    The BCL folks said that `ApplicationException` was a mistake - not useful like they hoped. They also said that `System.Exception` should have been `abstract`. – Jay Bazuzi Jan 05 '09 at 03:42
  • 2
    Non-nullable: It should be a compile error to pass a regular reference type T to a something that takes a non-nullable T! (just like you can't pass int? to int). Passing the other direction is fine, of course. – Jay Bazuzi Jan 05 '09 at 03:47
  • ApplicationException: its way too tempting to use an existing exception in the framework that exactly describes an error in your application, it just feels too much like duplication to create new one derived from ApplicationException. – AnthonyWJones Jan 09 '09 at 13:30
  • @AnthonyWJones - indeed. And if it exactly describes the error, what purpose would it serve? Upstream code would then have to catch the system's FooException, app-a's FooException and app-b's FooException with no continuity. – Marc Gravell Jan 09 '09 at 13:41
  • FWIW, what Eric Lippert wrote in the blog post you cited is total bullshit. There's nothing wrong with returning arrays. Arrays can be immutable or resizeable. Arrays are often ideal for parallelism and purely functional data structures suck for parallelism. The real design flaw behind the issue he raised is that .NET screwed the pooch when it came to data structures. – J D Feb 14 '10 at 17:49
  • 1
    @Jon Harrop: IMHO, there should have been support for immutable arrays, and for read-only array references. Possibly for some other array variants as well (e.g. a "resizable array" (indirect reference), or an array reference one with an offset and bound). – supercat Nov 19 '10 at 01:03
  • @Jon - somehow I missed your earlier comment. Let's just say that (and not for the first time) we have a difference of opinion. – Marc Gravell Nov 19 '10 at 04:33
  • @supercat: Exactly. *That* is the real design flaw in .NET. – J D Nov 22 '10 at 09:15
  • As far as I can make out, `IEnumerable.Reset()` exists purely to match `IEnumVARIANT` in COM. Personally, as someone who'd been hating it since `IEnumVARIANT`, I see its presence as a missed opportunity. – Jon Hanna Jan 12 '12 at 23:27
60

TextWriter is a base class of StreamWriter. wtf?

That always confuses me to the extreme.

jotik
  • 17,044
  • 13
  • 58
  • 123
Quibblesome
  • 25,225
  • 10
  • 61
  • 100
  • 21
    +1 I have to look it up every time. (Whaddaya *mean* I can't new TextWriter()?) – Nicholas Piasecki Jan 05 '09 at 23:02
  • 1
    Thank God...I thought it was just me. – I. J. Kennedy Dec 18 '10 at 03:08
  • ? It's always something that writes text, but only StreamWriter does so to a stream. Seems pretty straightforward. – Jon Hanna Jan 12 '12 at 23:28
  • 4
    The name StreamWriter doesn't make the fact that it writes text obvious enough IMO. It sounds in isolation like it would just write bytes and TextWriter would be a fancy implementation on top that would convert the api of (string toWrite) into bytes for you. Now if it was called StreamTextWriter then sure, it would be immediately obvious but a bit long :( – Quibblesome Jan 31 '12 at 18:35
44

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.

Scott Weinstein
  • 18,890
  • 14
  • 78
  • 115
  • Erm, how would it know what you're trying to create a new instance of? – BlueRaja - Danny Pflughoeft Apr 28 '10 at 15:15
  • 4
    @BlueRaja: Scott is referring to the naming of constructors in classes. `class Foo { new(int j) {i = j} int i; }` – dalle Apr 28 '10 at 18:28
  • While I agree 100% that ctor() or constructor() would have been better (not `New`--uppercase keywords are against convention), I hesitate to call it a design flaw. They wanted to attract existing C++/Java developers, and borrowing lots of stupid old syntactic conventions arguably helped them reach their goal. – Qwertie Feb 03 '12 at 23:10
  • related to this question: http://stackoverflow.com/questions/32101993/c-sharp-sorted-linkedlist there's no way (using only .NET) to simply sort a LinkedList with MergeSort, bucketsort or any other sorting algorithm but using linq that is ways slower than a ad hoc implementation. – CoffeDeveloper Aug 19 '15 at 17:53
39

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? :-)

Dai
  • 141,631
  • 28
  • 261
  • 374
naasking
  • 2,514
  • 1
  • 27
  • 32
  • 1
    With regard to #1, every type must either have some default value, or the system must provide a means of running a constructor whenever a variable or field of a particular type is allocated. I'd prefer the latter (an offshoot of #2) but #1 could be accommodated if non-virtual methods/properties could be decorated to specify that they should be called without a null-check. This would allow things like "String" fields to behave as though they default to an empty string rather than null (since String's static "length" function could return 0 if invoked on a null string). – supercat Nov 23 '11 at 21:04
  • 1
    With regard to #2, it would be useful in general if structs could specify not only a constructor other than fill-with-zero, but also a copy constructor other than byte-for-byte-copy. Actually, I'd really like to see a .net-like framework which could do a good job of recognizing that entities may have value or reference semantics, and allowing value-type heap objects to be tagged mutable, shared-immutable, or uncommitted (an uncommitted value object may be mutated if its mode is first CompareExchange'd to mutable, or may be shared if its mode is first CompareExchange'd to shared). – supercat Nov 23 '11 at 21:24
  • 1
    Excellent points! For #1, a type system annotation is the common solution, but I prefer propagating constructor constraints via type variables, like T:new(). Re:#2, good point about copy constructors, but I would be happy with more general constructors along the lines I described above. Even better would be to eliminate constructors-as-distinguished-methods entirely and simply make them static methods. This allows simpler and more general construction patterns, particularly if we allow static methods on interfaces. Constructors-as-static-methods + static-methods-in-interfaces solves #1 too. – naasking Dec 20 '11 at 17:56
  • Requiring zero-initialized value types and allowing bit-for-bit copies of value types may seem like a hassle, but it allows value types to be fast and elegant. Now, the fact that you can't bitwise-compare value types for equality--that's a design flaw. I really needed that feature for a functional data structure. – Qwertie Feb 03 '12 at 23:19
  • 3
    #3: What's the point of using a sealed class as a generic type parameter, e.g. Foo where T:string? #11: Okay, so I've got a `List` of a million Ts. How do you propose that the snapshot be taken efficiently? #21: Use the `readonly` keyword.... while there are some good suggestions here, they are mostly just that--suggestions, not design flaws. – Qwertie Feb 03 '12 at 23:29
  • 1
    #3: http://higherlogics.blogspot.com/2012/02/why-sealed-classes-should-be-allowed-in.html – naasking Feb 04 '12 at 06:38
  • 1
    #11: use immutable data structures, and simulate mutation only where needed. – naasking Feb 04 '12 at 06:39
  • #22: You can use the ctor snippet if you don't like typing public and the whole classname. The only downside is that they forgot to add a literal for the parameters of the constructor. – user886079 Jul 05 '15 at 22:06
  • 2
    This is a very interesting answer, but I think we should update with C# 6 features. Example: Items 19 and 21 were implemented =) – eduardobr Apr 20 '16 at 11:02
29

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

}
tuinstoel
  • 7,248
  • 27
  • 27
  • A generic type declares how you will use objects of that type, i.e. their interface. The constructor is an implementation detail of that interface, which is not the concern of the consumer. When you need to create a parameterized instance, use a factory. – Bryan Watts Jan 05 '09 at 05:10
  • For info, although it can't do compile-time checking, there is some code in MiscUtil for using non-default constructors (in generics) efficiently, i.e. without Activator.CreateInstance or reflection. – Marc Gravell Jan 05 '09 at 08:28
  • Because it makes no sense and is confusing on some uses. Nonetheless it can be useful when working with immutable objects. – Pop Catalin Jan 05 '09 at 08:43
  • This just gave me an idea... You could probably accomplish this by using the new Code Contracts feature of C# 4.0. Just add that constraint into your constructor. – davogones Feb 01 '09 at 05:57
  • 7
    In general, lack of member constraints is annoying, yes. – MichaelGG Feb 11 '09 at 21:21
  • 3
    amen, I've always wanted this – Steve Feb 21 '09 at 16:26
  • What would you use this for? You won't know what the params meant, right? – Jouke van der Maas May 08 '11 at 19:48
20
  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:-)

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.

JoshBerke
  • 66,142
  • 25
  • 126
  • 164
  • 1
    +1 I have to look up any of the XmlTextWriter, TextWriter, etc classes every single time. Same with the HttpWebRequest/Response stuff. Completely unintuitive API there. – Nicholas Piasecki Jan 05 '09 at 05:18
  • +1-1=0: XmlTextWriter etc, I agree its impossible to truely infer what they are from the name. HttpWebRequest I don't agree I find it quite intuative. – AnthonyWJones Jan 09 '09 at 13:40
  • To each his own I suppose. I guess with FTP I'd expect a higher level abstraction then what they have. – JoshBerke Jan 09 '09 at 18:29
20

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.

Robert Rossney
  • 94,622
  • 24
  • 146
  • 218
  • I wholeheartedly agree! This bugs me every time I have to use it (which is often). Argh! +1 – Eyvind Jan 09 '09 at 14:16
  • 2
    The least they could do would be to come up with a new DataSetV2 class (bad name - just for argument's sake), that used nullable value types instead of DBNull throughout. – Christian Hayter Jun 11 '09 at 12:20
  • Let's not forget the absurdity of requiring a special value, `DBNull.Value`, when `null` itself would have been perfectly adequate to represent NULL. Luckily LINQ-to-SQL just uses null for NULL. – Qwertie Feb 03 '12 at 23:01
  • Actually that absurdity is the rock upon which the entire absurd edifice was constructed. – Robert Rossney Feb 05 '12 at 00:34
15

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? :)

BenAlabaster
  • 39,070
  • 21
  • 110
  • 151
  • 3
    Microsoft should take Code Golf into consideration when it designs languages? – jrcs3 Jan 05 '09 at 00:14
  • MS should take VB code into consideration when it designs C# - there's quite a few 'nice features' in VB that are missing from C#! – gbjbaanb Jan 05 '09 at 00:43
  • @gbjbaanb: The problem is with their logic that "C# programmers wouldn't want those VB features because it's not like anything they've wanted historically". So they kept all the nice features back for the non-purist VB programmers :P – BenAlabaster Jan 05 '09 at 01:06
  • 1
    Is this a joke? How would the compiler know you meant Func instead of, for example, Func? – Ray Burns Nov 02 '09 at 23:05
  • 3
    @Ray Burns: How does it know in VB? VB supports it, so what's the difference? – BenAlabaster Nov 02 '09 at 23:52
  • 3
    @RayBurns Type inference? I've been using that since 1989. – RD1 Dec 02 '09 at 16:10
  • 3
    Lamdas are Homoiconic in C#. the fragment `(int x) => x * (x -1);` can mean `Func` or it can mean `Expression>` – Scott Weinstein Apr 13 '11 at 00:32
  • 3
    @BenAlabaster: VB supports late bound arithmetic operators. C# has to resolve them at compile time. It's a language difference. For example VB can add two objects together. C# cannot because `+` is not defined for `object`. – recursive Feb 29 '12 at 16:34
14
  1. 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.

  2. The ICollection.SyncRoot property:

    • Promotes poor design, an external lock is almost always more useful.
  3. Generics should have been there from the beginning:

    • The System.Collections namespace contains a lot of more or less obsolete classes and interfaces.
dalle
  • 18,057
  • 5
  • 57
  • 81
  • As for ToString(), it is called by the debugger for all objects so it had to be in System.Object anyway. – Joshua Jan 05 '09 at 17:37
  • So ToString() should exist because of the debugger? The debugger doesn't need ToString(), it could use IFormattable or just output ClassName 0xaddress if the object doesn't implement IFormattable. – dalle Jan 05 '09 at 18:41
  • 1
    1. Those methods are so common it was decided that the oddball cases were ok, does it matter if someone can call Object.Equals on your class? It is known that there may or may not be an implementation, and requiring : IEquatable, IFormattable on 99% of classes is odd. – Guvante Feb 11 '09 at 21:45
  • 2. How does an external lock differ from SyncRoot? If you are arguing against the naked lock() statement you are right there are many better alternatives, but it gets the job done. 3. Good point – Guvante Feb 11 '09 at 21:47
  • I agree with Guvante: the general rule is **always make the common case the default**, and classes in which `.ToString()`/`.Equals()` are useful is by far the common case. – BlueRaja - Danny Pflughoeft Apr 28 '10 at 15:20
  • 1
    It is useful to be able to e.g. construct a dictionary with user-defined objects as keys, using the default reference equality, without having to explicitly add code to the user-defined objects for that purpose. I'd consider Finalize to be a much bigger waste (a better alternative would have been to have objects which will need finalization implement iFinalizable and explicitly register themselves for finalization). OTOH, there should have been more inherent support for iDisposable, including calling Dispose if a constructor throws an exception. – supercat Nov 19 '10 at 00:50
  • 1
    @supercat: All which is needed is to update `EqualityComparer.Default` correctly. Then both `var dict = new Dictionary(EqualityComparer.Default)` and `var dict = new Dictionary()` will use reference comparison/equality. – dalle Nov 19 '10 at 06:23
  • @dalle: Checking on every lookup whether the key class for a Dictionary supports iEquatable would be expensive. On the other hand, the system could provide a static GetEqualityComparer(T thing) method which would return either "thing" (if it supported iEquatable) or a reference-comparison method if it didn't. That would only have to be called once when a Dictionary was created, rather than once for each lookup. – supercat Nov 19 '10 at 15:50
  • 1
    @supercat: What you describe is exactly what `EqualityComparer.Default` does. No need to check at every lookup. The comparer is a property for the `Dictionary` instance, and each `Dictionary` knows which one it is using. – dalle Nov 19 '10 at 19:16
  • @dalle: Thinking about it, if the decision of whether to regard keys as iEquatable were done when a collection were created, this could have odd effects if an iEquatable subtype were added to a collection of a non-iEquatable base type (e.g. stuffing "String"s into a "Dictionary"). One could argue that it's better to have all objects either use reference equality or iEquatable (absent an explicit iEqualityComparator) than have per-instance overrides of Equals and GetHashCode, but before generics came on the scene, how would one nicely code for that? – supercat Nov 19 '10 at 21:43
  • 1
    @supercat: A dictionary must use the most generic type (i.e. the common base class) as key, using both Strings and DateTime in the same dictionary would not make any sence unless using reference-comparison, unless a user-defined comparision is provied that is. Remember that the name of this topic is "C# (.NET) Design Flaws". – dalle Nov 20 '10 at 08:50
  • 1
    Prior to the advent of .net 2.0, every Dictionary would use an Object as a key; one couldn't know whether objects would support iEquatable unless one either run-time type-checked each one or had a parameter in the Dictionary constructor to say so. Having every Object support Equals and GetHashCode avoids this problem. Also, whether or not one should actually do so, one may add multiple very different types of keys into a dictionary with well-defined semantics. If not all types supported Equals and GetHashCode, that would not be possible (for better or worse). – supercat Nov 20 '10 at 21:42
  • 1
    @supercat: You still don't see it. A `Dictionary` can only hold keys of type `Key` or types derived from `Key`. All that the dictionary knows exist in the `Key` base type. What I stated is that "Generics should have been there from the beginning". If this would have been the case, this would not have been a problem. – dalle Nov 21 '10 at 11:54
  • 1
    @dalle: We agree that the lack of generics was responsible for many unfortunate decisions. I would aver, however, that GetHashCode/Equals were only available for iEquatable objects, there would be cases where best use of a collection would require individual items to be polled for iEquatable. How else, for example, could one maintain a collection of unique iDisposable objects, bearing in mind that a struct can implement iDisposable? It may be that such cases may be rare enough that the run-time cost of such comparisons would be acceptable, but it's still ugly. – supercat Nov 21 '10 at 17:08
  • 1
    @dalle: Incidentally, how does the speed of accessing an overridden base-class method compare with the speed of accessing an interface? My understanding is that accessing a base-class method is slightly faster. Not a huge difference, but equality tests are often done in loops that execute many times. – supercat Nov 21 '10 at 17:10
  • 1
    @supercat: If you are worried about speed, then take a look at `Dictionary` using .NET Reflector. It already use an interface for all comparisons, the `IEqualityComparer` interface. – dalle Nov 21 '10 at 19:05
12

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.

Greg Beech
  • 133,383
  • 43
  • 204
  • 250
  • There is a trick using Delegate.Create and some casting to do the conversion, but at least being able to do an explicit cast would be nice (I can understand the lack of support for implicit however) – Guvante Feb 11 '09 at 21:48
  • The design of delegates in general is flawed; for example the lack of weak events (implementing source-side weak events without special effort from the subscriber can only be done with a bunch of reflection and ReflectionPermission, see http://www.codeproject.com/Articles/29922/Weak-Events-in-C), and inefficiency that comes from the requirement that delegates must be reference types (delegates would have been faster, and would have used 1/3 as much memory in many cases, if they had been value types--then they would simply be a pair of pointers that you could pass on the stack.) – Qwertie Feb 03 '12 at 22:32
11

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.

Jason Jackson
  • 17,016
  • 8
  • 49
  • 74
ChrisW
  • 54,973
  • 13
  • 116
  • 224
11

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.

Daniel Paull
  • 6,797
  • 3
  • 32
  • 41
  • 4
    +1 for the on-target rant; i would add to this the inability to subclass every class, the lack of interfaces for fundamental classes, and the reluctance to fix framework bugs even after several years – Steven A. Lowe Jan 05 '09 at 04:23
  • 1
    If we cut to the chase, are we saying that the .Net frameworks are just so bad that it's hard to settle on just which flaw is the worst? I do feel better after a vent and appreciate the upvote as I was expecting to be shouted down by MS fan boys. – Daniel Paull Jan 05 '09 at 05:09
  • I haven't voted either way, and anyway I'm very reluctant to issue down-votes, but I'm trying to figure out why I JUST DON'T CARE. – Mike Dunlavey Jan 05 '09 at 13:40
  • 2
    I think you're saying "it's not as good as it could be" which is a non-answer. Nothing is perfect. Provide specifics. – jcollum Jan 05 '09 at 23:01
  • 3
    No, I'm saying that there are numerous instances where the design is obviously flawed, then when you think they get it right, they still get it wrong. For example, my post to MSDN forums here: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/f677b026-0719-47d7-a0b7-a0874df2ba17/ – Daniel Paull Jan 05 '09 at 23:28
  • @Will: you are a gentleman and a scholar. – Daniel Paull Jan 10 '09 at 00:19
  • This is so true! I came to C# world from Java & Kotlin. When I see how they implemented here in C# Range expressions & the Range type itself I literally want to jump out of a bridge. Just look how Kotlin range types & interfaces were designed. I can not believe that at Microsoft they could not figure out such simple & widely used constructs. – Michael P Aug 18 '20 at 15:51
11

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.

tuinstoel
  • 7,248
  • 27
  • 27
  • Actually I think it would be even better if it would require either a single statement or a block in curly brackets - like everything else in C#. Without the ability to fall through, the current break syntax is a bit dodgy and it doesn't limit to scope either. (AFAIK, it might, I don't know) – Tamas Czinege Jan 24 '09 at 13:16
  • I don't understand what you mean. Post your own 'ideal' switch statement here. I don't want fall through but comma separated values. – tuinstoel Jan 24 '09 at 13:35
  • DrJokepu: fall-through exists, even for non-empty clauses. You only have to make it explicit, by using `goto case`. – Konrad Rudolph Jan 24 '09 at 18:09
  • 8
    By the way, I agree with OP. `switch` is fundamentally broken in all languages that emulate C's deliberatly crippled version (optimized for speed!). VB fares much better, but is still light-years behind languages with pattern matching (Haskell, F# …). – Konrad Rudolph Jan 24 '09 at 18:12
  • Konrad: You're right about goto of course - and probably that's the reason of still using the C switch syntax in C# – Tamas Czinege Jan 24 '09 at 21:55
  • 1
    tuinostel: something like switch (a) { case 1 { do_something; } case 2 { do_something_else; } } - that is, getting rid of the break statement *and* requiring proper code blocks for each case – Tamas Czinege Jan 24 '09 at 21:58
  • 2
    Having break seems like a mistake in general, isn't it a compile error to emit it? It seems to only be there to ease the transition from C to C# (aka teaching devs that they can't auto fall through) – Guvante Feb 11 '09 at 21:41
  • Here here! Make the common case the default! – BlueRaja - Danny Pflughoeft Apr 28 '10 at 16:19
10

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?

Thomas Eyde
  • 3,820
  • 2
  • 25
  • 32
  • 1
    It is annoying that there is not sugar for this when you want it, I see why they do it tough, it encourages not instantiating the event's arguments if not needed – ShuggyCoUk Feb 21 '09 at 15:57
  • Hm. I either don't understand or disagree, or both :-). I can't say I ever felt discouraged to instantiate anything. This smells like premature optimization to me. – Thomas Eyde Feb 21 '09 at 21:05
  • Partial methods are a viable alternative, in some cases. – Robert Harvey May 29 '09 at 16:36
  • PLUS all the coupling this causes... MS has CAB which fixes both of these problems, but CAB has many problems of its own due to limitations in C# (eg. strings rather than enums as event-topics)- **why not just make loosely-coupled events part of the language**!? – BlueRaja - Danny Pflughoeft Apr 28 '10 at 16:20
9

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.

ShuggyCoUk
  • 36,004
  • 6
  • 77
  • 101
  • This is especially flawed, because it goes against intuitive expectations of O behavior ans so will trap many people – oefe Feb 21 '09 at 16:32
7

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

ggf31416
  • 3,582
  • 1
  • 25
  • 26
  • Well, you cannot change the number of elements in an Array, so there's nothing that Add, Clear, Insert, and Remove(At) can do but throw NotSupported... In fact, i would expect ANY implementation of IList that returns true for IsFixedSize would throw on them. – C.B. Apr 25 '13 at 13:53
  • @C.B. a bit late to the party I guess :) But If Array cannot satisfy "IList", why implement it anyway? That's a violation of L in SOLID principle. – wingerse Aug 28 '17 at 14:47
7

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.

Jay Bazuzi
  • 45,157
  • 15
  • 111
  • 168
  • 1
    Isn' this very similar to your other suggestion? – RCIX Jun 30 '09 at 02:12
  • 1
    No, this one is about the C# language and the other is about the framework. Not everyone cares about the distinction, so let me say that this one is about what is allowed, and the other is about what is provided. – Jay Bazuzi Jun 30 '09 at 03:24
6

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.

jasonh
  • 29,297
  • 11
  • 59
  • 61
  • I wouldn't mind if events were not thread-safe by default (it could increase performance in single-threaded code); what's silly is that add/remove are safe by default, but that the natural way to fire an event is unsafe, and there is no facility to easily make it safe. – Qwertie Feb 03 '12 at 21:57
  • @Qwertie: What's sillier is that for quite awhile, add/remove would use locking and yet still not be thread-safe. – supercat Jul 06 '12 at 21:01
6
  • 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.

J D
  • 48,105
  • 13
  • 171
  • 274
  • There is no structural typing in the CLR, but you seem to have structural typing mixed up with type inference or with the Ruby feature known as "symbols". Structural typing would be if the CLR considered Func and Predicate to be the same type, or at least implicitly convertible. – Qwertie Feb 03 '12 at 22:07
  • Speaking of comparison, don't forget Comparer! – Qwertie Feb 03 '12 at 22:08
  • @Qwertie I was referring to programming language features like polymorphic variants in OCaml. OCaml's LablGL library has many interesting examples of structural typing being useful in the context of graphics. Nothing to do with type inference and only tangentially related to symbols. – J D Feb 05 '12 at 13:29
  • 1
    How would one use an immutable-struct `IEnumerator`? – supercat Jul 06 '12 at 21:00
5

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)

Michael Buen
  • 38,643
  • 9
  • 94
  • 118
5

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.

Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
  • 3
    The `DateTime.Now` one is the world's most obvious race-condition, but +1 for the rest – BlueRaja - Danny Pflughoeft Apr 28 '10 at 18:49
  • It is not so much that it is a race condition, but the fact that they made it a property. Properties look exactly like fields, so it is rather surprising behavior IMO. – Brian Rasmussen Apr 29 '10 at 06:53
  • 4
    @Brian Rasmussen: DateTime.Now is quite properly a property, since it is changed not by reading it, but by outside factors. If one reads a property like SomeForm.Width, and then--after the user has resized the form--one reads it again, the value on the second read will be different. While it is possible that the first DateTime.Now might take long enough to execute that it would influence the value read by the second, such effect would be no different from any other function whose execution took the same amount of time. – supercat Nov 23 '11 at 19:27
4

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

Nicholas Piasecki
  • 25,203
  • 5
  • 80
  • 91
  • 3
    I believe they provide the ISerializable interface and implicit constructor to do things like that, aka when you don't want the serializer to just call the properties. While a bit more work, it looks like you are already doing most of that work with your method. – Guvante Feb 11 '09 at 21:37
4

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

Mendelt
  • 36,795
  • 6
  • 74
  • 97
  • 5
    Interesting, but i prefer extension methods. If i get a library that contains a bunch of extension methods for strings, i don't want to have to change all of the string references to MyMixin in order to get the new stuff. It's kind of minor, sure, but that transparent addition of methods is what makes extension methods so nice. – RCIX Jun 30 '09 at 02:07
  • BTW did you know that that works already? – RCIX Jun 30 '09 at 02:16
  • 2
    I don't see how LINQ could work this way – BlueRaja - Danny Pflughoeft Apr 28 '10 at 16:17
  • 2
    @RCIX: Mixins sound like they way I've thought extension methods should work. The problem with making extension methods implicit is that it means real class members need to have priority over extension methods. If an extension method Graphics.DrawParallelogram(Pen p, Point v1, Point v2, Point v3) is defined and later a DrawParallelogram function is added to System.Graphics which uses points in a different order, the code using the extension method will break without warning. BTW, would there have been any problem using two dots for extension methods (e.g. object..method()?) – supercat Nov 19 '10 at 00:56
3

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.

Dave Markle
  • 95,573
  • 20
  • 147
  • 170
3
  • 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.

Ray Lu
  • 26,208
  • 12
  • 60
  • 59
  • 3
    Very handy, though - I have a "ThrowIfNull` extension method for parameter checking ;-p – Marc Gravell Jan 04 '09 at 23:51
  • 2
    you can do this? ugh ThrowIfNull it is interesting extension but this just seems wrong. – JoshBerke Jan 05 '09 at 00:21
  • 1
    In plain CLR you can Invoke instance methods on null references and if the method doesn't access the object or it's fields then the call doesn't throw null reference exception. (You can't do this in C# as it uses callvirt even for non virtual methods) – Pop Catalin Jan 05 '09 at 00:26
  • I ranted a bit about that exact thing over at http://blog.freakcode.com/2008/09/hazards-of-extension-methods.html – Markus Olsson Jan 05 '09 at 01:24
  • You can pass null values to static methods, for which extension methods are just syntactic sugar. Your change would alter semantics, especially for interoperability in languages without extension method support. – Bryan Watts Jan 05 '09 at 04:08
  • Yeah, agree and it is very handy. – Ray Lu Jan 05 '09 at 21:43
  • 7
    the exception thing is dead wrong. You can't fail fast if you HAVE to catch every goddamn exception that might be thrown in the call stack. But I DO wish it were much easier to identify any and all exceptions that might be thrown in a particular call and its resulting callstack... –  Jan 09 '09 at 13:53
  • 1
    @Will I agree whole heartedly, checked exceptions just make developers add "throws Exception" more often than not. And in most real cases it causes more ambiguity than without it. – Guvante Feb 11 '09 at 21:42
  • 3
    @Will: Exception handling is icky in both Java and .net, since the mechanism used intimately ties together three concepts which are somewhat related but also somewhat orthogonal: (1) What type of thing(s) went wrong (an array bounds error, an I/O timeout, etc.); (2) Whether certain code should take action as a result; (3) At what point the problem should be considered "resolved". Consider a routine which is supposed to mutate an object with data read from an IEnumerable. What should happen if an exception occurs in the processing of that IEnumerable? – supercat Nov 23 '11 at 20:37
  • 1
    @Will: Fundamentally, there are at least two orthogonal questions for the caller to worry about: (1) Whether the evaluation of the IEnumerable has harmed the system state in some way, and (2) To what extent the incomplete behavior of the mutating method has or has not affected the object being mutated, and whether it may in any way have harmed the system state outside that object. There is no nice way for both types of information to be provided back to the caller, unless the code which uses the IEnumerable has a means of knowing which exceptions from it represent outside state corruption. – supercat Nov 23 '11 at 20:51
3

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.

Joshua
  • 40,822
  • 8
  • 72
  • 132
  • 1
    What obvious framework bugs do you mean? – Robert Rossney Jan 05 '09 at 07:29
  • 1
    #1 Click on a partially visible child control of a scrollable control. The control is shifted into view before it receives the MouseDown event, causing the click to be somewhere else on the control than expected. It's worse on tree views, where it triggers a drag operation as well. – Joshua Jan 05 '09 at 17:05
  • 1
    #2 [this](http://stackoverflow.com/questions/241134/what-is-the-worst-c-net-gotcha/2595746#2595746) #3 [this](http://stackoverflow.com/questions/241134/what-is-the-worst-c-net-gotcha/2731387#2731387) #4 [this](http://stackoverflow.com/questions/241134/what-is-the-worst-c-net-gotcha/1404987#1404987) – BlueRaja - Danny Pflughoeft Apr 28 '10 at 16:35
3
  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 (e.g. 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.
Community
  • 1
  • 1
Qwertie
  • 16,354
  • 20
  • 105
  • 148
  • I agree with you about a subsets of `IList`, though I'd use `IReadableByIndex` and `IAppendable`. Many of your other things are squawks I'd agree with too. – supercat Jul 06 '12 at 20:56
  • That's a really long name. Maybe we could compromise on `IListReader` ;) - I use the word "source" as the antonym of "sink" (a write-only interface). – Qwertie Jul 18 '12 at 19:16
  • Maybe `IListSource` or `IReadableList`. There can be value in having base interface types include methods which don't exist in all derivatives, though I think it's often good to have interfaces be somewhat specialized. For example, one could have an `IList` which contains resizing methods which may or may not work, and an `IResizableList` which implements the same methods, but guarantees they should work. Such an approach may be useful in cases where a field might either hold the only extant reference to a mutable list, or a shared reference to an immutable one. – supercat Jul 18 '12 at 19:25
  • In such a case, code which wants to change the contents of the list would check whether it's a mutable type and, if not, generate a new mutable instance containing the same items as the immutable list and then start using that. It would be irksome if the code had to constantly typecast the field every time it wanted to use a mutating method on it. – supercat Jul 18 '12 at 19:27
  • @supercat That's only irksome because C# doesn't provide a really easy way to check that an interface is implemented and immediately use it. MS should add a language feature to make it easier. My preferred technique would be a binding expression, `if (rl:(list as IResizableList) != null) rl.Add(...);`, but there are other proposals. As the author of various collections and collection adapters, what irks me is writing lots of dummy methods that throw exceptions. As a type safety fan, I don't want to be allowed to call illegal methods. An IntelliSense fan, I don't want to see them listed. – Qwertie Aug 21 '12 at 23:03
  • The pattern scenario you describe could be handled pretty easily without a new language construct: `{var rl=list as IResizeableList; if (rl!=null)...`; the problem is that it requires boxing. More interesting would a way of saying "Invoke a method with generic-constrained parameters on a generic object if possible, or do some other action if not". I think a compiler could do that without run-time changes if it created a generic static class for each such situation. For example (using human-readable names) a `MaybeDisposer` with a public static `Dispose(ref T item)` method... – supercat Aug 22 '12 at 18:32
  • ...which would call a `DisposeIt` delegate. That delegate would initially be set to a routine which would check whether `T` (the class's generic type parameter) implemented `IDisposable`; if it did not, it would set the delegate to a do-nothing routine; otherwise, it would use Reflection to create a delegate to call a routine with a parameter constrained to `IDisposable`. The cost for calling such a routine, after the first usage with any particular type, would be the cost of a single call through a static delegate. No changes to the .net run-time would be required. – supercat Aug 22 '12 at 18:36
  • The solution to the "annoying dummy methods" problem would be to allow an interface `IFoo` to designate a static class `IFooHelpers` such that if .NET loads a class which claims to implement `IFoo` but lacks `int fnord(string st)`, the Runtime would auto-generate `int IFoo.fnord(string st) { return IFooHelpers.ClassHelpers.fnord(this, st); }`. Add a means for an interface to specify the Strong Names of other interfaces with which it should be considered compatible, and many longstanding problems could be fixed easily. – supercat Jun 30 '15 at 19:55
2

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.

Kev
  • 118,037
  • 53
  • 300
  • 385
1

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 } 
tuinstoel
  • 7,248
  • 27
  • 27
0

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:

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

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)

lomaxx
  • 113,627
  • 57
  • 144
  • 179
  • Definitely anon types (new {...}), not implicitly typed (var) – Marc Gravell Jan 05 '09 at 04:56
  • Just re-read what I posted and it was wrong. I did mean implicitly typed variables tho – lomaxx Jan 05 '09 at 22:48
  • 1
    Eric Lippert explained why var cannot be used outside of methods, it is basically because it creates a black box of possibilities. http://blogs.msdn.com/ericlippert/archive/2009/01/26/why-no-var-on-fields.aspx – Guvante Feb 11 '09 at 21:52
  • 1
    var is not intended to be variant! it is precisely for the shorthand (especially anon types). enjoy dynamic when it comes along... – ShuggyCoUk Feb 21 '09 at 15:56
0

The StreamWriter and StreamReader classes (and subclasses) closing the underlying stream on Close() and Dispose(). Can't count how many times I worked around this design choice.

GaussZ
  • 838
  • 10
  • 25
  • 1
    The proper design would have been to provide an *option* for whether the the `StreamReader` or `StreamWriter` should assume ownership of the underlying stream, since there are cases where it should and other cases where it should not. – supercat Jul 06 '12 at 20:57
-1

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.

Filip Ekberg
  • 36,033
  • 20
  • 126
  • 183
-1

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...!! :(

Thomas Hansen
  • 5,523
  • 1
  • 23
  • 28
  • Multiple Inheritance is handled 99.9% of the time by interfaces, and 99% of uses of it in languages that support it are clunky and confusing. – Guvante Feb 11 '09 at 21:50
  • @Guvante - I agree. Multiple inheritance rarely makes code simpler or easier to understand and maintain. (Indeed, single inheritance is only rarely a good idea, and increasingly people seem to be recognizing that there are better ways of reusing code.) – RD1 Dec 02 '09 at 16:32
  • @Guvante: I would say it's handled 95% of the time with interfaces. However, with interfaces **and** extension methods, it's handled 100% of the time. Hurray! – BlueRaja - Danny Pflughoeft Apr 28 '10 at 18:58
  • multiple inheritance is useful, and one important missing feature of the c# language. However a proper implementation requires removal of method overloading. Indeed, a proper implementation of MI makes all the supposed disadvantages of MI to disappear and make the code reuse via inheritance simple without any boilerplace code to satisfy the compiler – Kemal Erdogan Mar 14 '13 at 08:58