Rant ahead. Criticisms of the language and the language idioms are interspersed.
Disclaimer: I've been wrong about C# before. My lack of understanding may be caused by my lack of knowledge. If I am proven wrong, then I become a better programmer, which I appreciate, so contradict me freely.
Why do we do this to ourselves?
We don't use "goto", of course, because we want our iteration to be structured.
But "for" is only for collection iteration with indexing. For everything else, we use "while", even though "for" gives the reader the accumulator variable, means of accumulation, and end condition upfront. And then we scatter in a few "break"s to confuse the hurried reader further.
We use "for" instead of "foreach" for performance
On lists that could have been arrays. We always call "ToList", never "ToArray".
We don't need discriminated unions. Classes and interfaces are enough for any situation.
And then we "switch" on them and have to repair bugs having to do with missing cases that were created later.
We don't use static fields and singleton classes. Instead, we use dependency injection frameworks to ensure that only one instance of each class is generated and that all other classes have access to that instance. Which is different.
We don't have an order of compilation below the project level. Every class in a project, unless it is private to another class, is accessible from every other class. We deal with circular class dependencies by not worrying about them. Code goes wherever happens to feel best at the moment.
"IConvertsFooToBar" is supposed to be prettier than "Func<Foo, Bar>" for some reason.
Granted, "Func" is an ugly word. But I guess we had already used up all the punctuation elsewhere.
And we need to have separate "Func" and "Action" delegates since you can't have a value of type "void".
We use static typing so that we can be sure that all representable cases are valid.
Unless they're null. Anything could be null. (Except structs, but we don't use structs.)
"default". Without "default", you have to make two versions of every generic method, one for classes and one for structs. With "default", we still have to make two versions of every generic method since the two have completely different behaviors. For classes, it returns "null", which might blow up your program if you're not on guard. For structs, it returns an instance in which every field is set to its own "default", which is significantly worse since it's very difficult to detect.
Exceptions are unexceptional. We wrap deep calls in try/catch because something in there will probably throw an exception at some point, and just because there was an exception doesn't mean that we need to crash now.
Everything is a "manager". "Manager" is pretty close to synonymous with "class" when you think about it.
I'm tired. I miss programming functionally.