Any product or framework evolves. Mainly it's done to catch up the needs of it's users, leverage new computing powers and simply make it better. Sometimes the primary design goal also changes with the product. C# or .net framework is no exception. As we see, the present day 4th version is very much different comparing with the first one. But thing comes as a barricade to this evolution- backward compatibility.

In most of frameworks/products there are features would have been cut off if there was no need to support backward compatibility. According to you, what are these features in C#/.net?

Please mention one feature per answer.

  • Keyword new Mar 3, 2011 at 20:19
  • 4
    The question is clearly not constructive according to the guidelines, and clearly constructive based on the fantastic answers. Voting to re-open.
    – psr
    Oct 27, 2011 at 23:11
  • 1
    shame it closed, still maybe Eric will blog this instead of answering here?
    – jk.
    Oct 28, 2011 at 14:29

12 Answers 12


Anonymous methods. I think everyone agrees that the anonymous method syntax chosen for C# 2.0 is chunky and clunky compared to the lambda syntax we added to C# 3.0. It is deeply unfortunate to have two almost-identical syntaxes to do the same thing.

  • 1
    Agreed, anonymous methods were great when we didn't have lambdas...now they're just redundant. Jan 28, 2011 at 18:56
  • 9
    There is one special feature of anonymous methods thats far better than lambda expressions .... their perfect name !!
    – explorest
    Jan 28, 2011 at 19:36
  • 1
    At this point, I don't even remember the syntax for an anonymous method in C#. I'd have to look it up if for some bizarre reason I needed to use one instead of a lambda.
    – Kyralessa
    Oct 10, 2019 at 14:07

I'd get rid of the Non-generic collections. They are an abomination...and there are too many cases where I'm using linq and have to do something like

var customObjects = container.CustomObjects.Cast<CustomObject>();

Every time I have to do that, a small part of my soul dies.

  • This is too great.
    – user2528
    Jan 27, 2011 at 4:47
  • 1
    Not all ports of .NET support Generics. .NET Micro is an example. May not apply if CustomObjects will never move to this platform. Jan 28, 2011 at 16:23
  • 2
    That's the advantage to not having a soul.
    – CaffGeek
    Oct 27, 2011 at 20:43

void as a type. Why on earth is "void" a type? It has no instances, it has no values, you can't use it as a generic type argument, formal parameter type, local type, field type or property type. It has no meaning as a type; rather, it is a fact about what effect a method call has on the stack of the virtual machine. But the virtual machine is just that: a virtual machine. The real machine will put the returned value in a register (typically EAX on x86) and not affect the stack at all! Void as a type is just a bad idea all around.

Worse: when used in a pointer type as in void* it means something completely different than what it means when used as a return type. Now it means "a pointer to a storage location of unknown type", which has nothing whatsoever to do with its meaning as "a method that doesn't return any value."

We can replace void* as a pointer type with IntPtr. (And void** with IntPtr* and so on.) We can replace void as a return type with "Unit", a type that has a single value, namely, null. An implementation of the CLR could then decide that a unit-typed function call could optimize its usage of registers or stacks appropriately, knowing that the null that is being "returned" can be safely ignored.

In such a world you no longer need separate Func<A, R> and Action<T> delegates. Action<T> is just Func<T, Unit>.

  • 4
    ... and Task is just a Task<Unit>. Reimplementing the library bits of the async CTP made me really wish for this...
    – Jon Skeet
    Oct 27, 2011 at 19:24

The empty statement ;. Error-prone, almost always a typo, and gives you no added meaning that is not already expressed by {}.

  • 7
    Not to mention the huge performance penalty under the 64-bit JIT </snark>. Jan 28, 2011 at 19:43
  • 7
    An empty statement has a performance penalty? Why is that? Oct 27, 2011 at 20:07

Unsafe covariance on arrays of reference type. With typesafe covariance on IEnumerable<T>, at least some of the need for array covariance has gone away. (If we had a covariant read-only list interface then we wouldn't need it at all.)

  • I was absolutely going to write this down when I saw the question title -- you beat me to it :( Jan 28, 2011 at 16:24
  • 1
    The ability to receive a covariant array reference and safely write to the array references that were read from it is useful. If like IList<T> inherited from IReadableList<out T> and a non-generic IPemutable, it could support things like sorting, but arrays support it more easily.
    – supercat
    Mar 12, 2014 at 6:52

The unary plus operator. Least useful operator of all time. If we didn't have to keep it for backwards compat, I'd take it out in a heartbeat. Who uses this thing, anyone?

(Clarification: The unary plus operator +x is not the preincrement operator ++x, not the postincrement operator x++ and not the binary addition operator x+y.)

  • 1
    for (int i=0;i<ceiling;i++) Jan 28, 2011 at 19:02
  • 13
    He's not talking about the preincrement and postincrement operators: he's talking about the unary plus, the opposite of the negative sign on a number: +1 == 1. It's pretty darn close to a no-op. Jan 28, 2011 at 19:31
  • 9
    It's not just about the effect on the machine readable output, it can improve the look of code for humans: if(a == +1 || a == -1){...}. Feb 5, 2011 at 13:26
  • 5
    @ShuggyCoUK: What is particularly bizarre about it is that it is overloadable. Because that happens a lot. You know, how you're writing some JavaScript and you think man, I wish JS allowed me to make user-defined unary plus operator semantics like C# does. Nov 1, 2011 at 15:50
  • 2
    @Eric I did not realise it was overloadable. wow you could do some nasty obfuscation with that :)
    – ShuggyCoUk
    Nov 1, 2011 at 16:08

Defaulting numeric literals to double

For most business apps, decimal is more appropriate anyway... or maybe it would be better to just remove the idea of a default, and force developer to actually make the choice.

(That "remove the default" would be appropriate for some other things, too. For example, I've given up trying to persuade everyone that classes should be sealed by default, but I suspect it's easier to persuade people that they should think about whether their new class should be sealed or not, and make it explicit.)

  • I'd perhaps lean to identifying literals that imply a value that cannot be represented as a double/float precisely...
    – ShuggyCoUk
    Nov 1, 2011 at 16:10
  • If they're into Java - refer them to the sacred texts by Joshua Bloch "design or document for inheritance or else prohibit it" martinfowler.com/bliki/DesignedInheritance.html IIRC kotlin does seal classes by default, so the industry is moving into a better direction in this respect. Aug 29, 2012 at 18:48
  • Why should classes be sealed?
    – James
    Jan 6, 2013 at 3:34
  • @James: For exactly the reasons Josh gives. Designing a class which enables inheritance in a sensible, consistent way takes time - and doing so without knowing how inheritance is going to be used is even worse.
    – Jon Skeet
    Jan 6, 2013 at 8:46
  • 1
    @Pacerier: Think about immutability, for example. A non-sealed class can't claim to be immutable, as subclasses can introduce mutability.
    – Jon Skeet
    Aug 30, 2014 at 7:17

This is more a guideline than a feature, but I think it's important because it's already too much ingrained in people's minds as the canonical and best solution:

The official pattern to implement IDisposable.

It's cumbersome and there're better ways.


I know there are big differences between the various timer classes. But couldn't we get rid of one or two of them, anyway?


Methods and types of Array and List<T> that became obsolete with Linq, for example:

  • Array.TrueForAll can be replaced with Enumerable.All
  • Array.FindAll can be replaced with Enumerable.Where
  • List<T>.ConvertAll can be replaced with Enumerable.Select
  • Predicate<T> can be replaced with Func<T, bool>
  • Converter<T,R> can be replaced with Func<T, R>
  • IComparer<T> really should be a delegate Func<T, T, int>
  • 3
    I like the Predicate<T> because it captures the intend on how the function is used and saves a tiny bit of typing.
    – Zebi
    Jun 19, 2011 at 15:41

Non-generic Delegates Like non-generic collections, non-generic delegates are useless now that we have Func and Action series. And I would have cut off the variants at three parameters. If you have more than three parameters, make a structure and use that as the single parameter. Declaring a specific delegate for event handling is not very DRY.

  • 3
    How would you define a delegate to a method that takes a ref int with Action or Func? How would you define a combinator with Func? That is, there's no way to say "delegate D D(D d);" with Func. Jan 28, 2011 at 20:38
  • 2
    hmmm...I stand corrected. That's why I leave the job of creating the tools I use in your capable hands ;) Jan 28, 2011 at 21:02
  • @EricLippert: I'd like to see a special delegates class which would through CLR magic contain delegates of "every" arity and combination of value/reference parameters [searching for a delegate in the magic class with a suitably-formatted name would create the type], and have all delegates of proper signature inherit from that. Code which wants a particular delegate would still require an exact type, but code which wants a particular signature could use the magic name.
    – supercat
    Mar 12, 2014 at 6:49

asmx Web Services

I think they are quite obsolete with WCF nowadays.

  • 6
    Yeah, but sometimes much easier to grok . . . Jun 3, 2011 at 21:45

Not the answer you're looking for? Browse other questions tagged or ask your own question.