5 Things I Hate About C#
They say that you don't really know a language fully until you are able to list at least five things that you really hate about it. Overall, I would rather work with C# then other C-esque languages that are around, but that being said, there are things about it that irritate the crap out of me.
1) ??, the null coelescing operator
Ok, so && means and, and || means or in an expression. For null coalescing, you can use the ?? operator. It will evaluate the left side, if it is not null, it will use it, else it will use whatever is at the other side of the ??.
string a = "foo";
string b = "bar";
var c = a ?? b;
Console.WriteLine(c); // prints foo
a = null
var d = a ?? b;
Console.WriteLine(d); // prints bar
Both straight forward, and a great language feature. Here is my question, why not use the or operator for what is essentially an or operation? In ruby, the previous block would be done like this:
a ='foo'
b = 'bar'
c = a || b;
puts c # prints foo
a = nil
d = a || b
puts d # prints bar
To me, that is a hell of a lot more readable. You can also do things like
function_one && function_two # only calls function two if function two returns a "truthy" value
function_one || function_two # only calls function two if function one returns a "falsy" value
While abuse of that can lead to unreadable code, but if you are working at a fairly high level in your code base, this can make things both more terse and more readable.
It may seem odd to have such a strong feeling over semantics, but I find it irritating to see a fantastic idea that could be so elegantly miss the mark for such a simple reason.
The idea of "truthy"/"falsey" values leads us to point two:
2) Why is null not an object?
Ok, so this mostly stems from typing string.IsNullOrEmpty(s); about a billion times a day, which is a slight improvement over the older (and more frustrating)
if (string.IsNullOrEmpty(stringVal)) {}
becomes
if string_val.empty? {}
better yet, in python, all objects have a method that defines boolean coercion, where both nil and "" are considered false. so the previous example is
if string_val:
That is what I want to see
irb(main):001:0> NilClass.methods
=> ["inspect", "instance_method", "private_class_method", "const_missing", "clone", "public_methods", "public_instance_methods", "display", "instance_variable_defined?", "method_defined?", "superclass", "equal?", "freeze", "included_modules", "const_get", "methods", "respond_to?", "module_eval", "class_variables", "autoload?", "dup", "protected_instance_methods", "instance_variables", "public_method_defined?", "__id__", "method", "eql?", "const_set", "id", "singleton_methods", "send", "class_eval", "taint", "frozen?", "instance_variable_get", "include?", "private_instance_methods", "__send__", "instance_of?", "private_method_defined?", "to_a", "name", "type", "<", "protected_methods", "instance_eval", "object_id", "<=>", "==", ">", "===", "instance_variable_set", "kind_of?", "extend", "protected_method_defined?", "const_defined?", ">=", "ancestors", "to_s", "<=", "public_class_method", "allocate", "hash", "class", "instance_methods", "tainted?", "=~", "private_methods", "class_variable_defined?", "autoload", "nil?", "untaint", "constants", "is_a?"]
Now that is a useful nil
3) Consistency
Generics and type inference are both wonderful things, but their implementations are quite inconsistent. Type inference systems have been around for ages now, why can't C# infer method signature types? Or generic types?
Better yet, properties are just sugar, the compiler will turn
string Prop { get; set; }
into
public string get_Prop(){}
public void set_Prop(string value) {}
So, considering that they are just methods, why cant you do
Prop<T> { get; set; }
while you can do
public T GetProp<T>() {}
Same deal with ref and out, properties are treated as something totally different, while behind the scenes it is just another set of methods.
This isn't the sort of thing you
4) Oddly Named Methods
There are certain concepts when it comes to functional style programming that are pretty much universally understood, like map, reduce, and filter. If you talk to a programmer about a map function, it doesn't matter if he uses python or if he uses java, he will know what you are talking about. Not so for the .net guy
Map becomes Enumerable.Select, reduce/fold/accumulate becomes Enumerable.Aggregate, and filter becomes Enumerable.Where. Why? Who the hell knows.
5) Poor Engineering in the Standard Library
Three basic ideas that are almost universally considered to be true are that you should favor composition over inheritance, the Liskov Substitution Principal, which states that objects should be open for extension, but closed for modification, and the Interface Substitution Principal, which means you should use one interface per logical group of functionality, not just one mother interface.
The BCL favors inheritance over composition, uses sealed like some sort of mantra, rarely (if ever) uses the virtual keyword, and seems to love the idea of interfaces with about a billion methods (ever try to implement a membership provider interface?)
Generally when you have an "Everything and the kitchen sink" type libraries from a single vender, you end up with some sort of predjudice or blind spots that go through things. In java, the problem is over engineering. .Net is much more practical and "down to earth" (for lack of a better term), but there are some places that makes testability and flexibility a huge pain to implement.
Conclusion
Again, this isn't to bash C# or .Net. There are a great deal more then five things that I do like about it. The fact that it is an evolving language, especially compared to java which hasn't really moved at all in the last few years. The pragmatic approach is definately appreciated when it comes to getting things done quickly. As someone who has been using it professionally for a few years now, these are just personal gripes.