C# hate



  • The "Which language is the least bad" thread has predictably turned into a C# love fest. And with good reason - the language is just very good. But not perfect.

    So here's a new subject before Blakey's 2nd law derails us again: what are some of the things you HATE (or just dislike) about c#?

    Here are some peeves off the top of my head.

    • Non-generic collections. I know this was fixed in .NET v2.0, but really... WTF were they thinking?

    • Poor syntactic sugar for auto-properties. You can make a public non-initialized property using:
      int X { get; set; }
      This can declare your standard getter/setter and implicit hidden member. You can also declare its access level. And that's it. No auto-initialization. No custom hidden value. If you want any of that, you're back to writing the accessors yourself (some improvements are coming soon on this front).

    • The inability to safely call overloaded methods from constructor. Wouldn't it make more sense that base class call base implementation of the method?

    • Related to previous one: why is the only way to call overloaded constuctor in the declaration? Why can't I call it with this.base(), like I can overloaded methods? There are probably good answers for this and previous one, but it still feels un-intuitive and stodgy.

    • Finally, my big one, the thing I hate the most about C#:

    ##LINQ

    Now hold off your pitchforks for a moment. I don't mean LINQ as a concept or the extension methods that form the base of it. Those are great. I mean the unholy mess of spurious keywords that allow you to write stuff like this:

    var greaterThan5 = from x in data where x.Count > 5 select x;
    

    Sure, this look nice and safe if you're a DB coder and are used to SQL. But is that worth adding 50 fucking special keywords into the language? Not to mention that in most real world cases they still aren't enough and you end up having to mix in extension methods anyway. This of course crates an even uglier mess than if you only used extension methods in the first place.

    I mean, what's wrong with this:

    var greaterThan5 = data.Where(x => x.Count > 5);
    

    It's clear, easy, using nice functional syntax. Number of spurious keywords needed: 0. Amount of happiness lost due to not looking like SQL: 0.

    LINQ extension methods are great, but they never should have bothered with LINQ pseudo language. BIG mistake. IMO.



  • Not really hate, but I would love to be able to do something like int X { get; notify; } which automatically does the whole INotifyPropertyChanged thing.



  • I'm using some kind of ReSharper macro thing for that. I made one for DependencyProperty in WPF and it barfs out like two pages of code for one property. That's some WTF there, but that's more to do with framework, so not strictly a C# thing.



  • I tend to stay away from plugins as I usually only have the Express versions installed. Just seems odd as I very rarely put anything inside a setter except the binding notifier.



  • @coldandtired said:

    Just seems odd as I very rarely put anything inside a setter except the binding notifier.

    For standard properties, sure. It's just that INotifyPropertyChanged isn't a part of C# itself, but .NET framework. So, I'm not sure they can just bake it into the language using a keyword (like you suggested).

    Dependency property is, however, a completely different thing.



  • IDisposed is a .NET thing yet C# has support for using blocks. Also the whole LINQ thing.



  • @mott555 said:

    IDisposed is a .NET thing yet C# has support for using blocks. Also the whole LINQ thing.

    Hmm, ok. Fair enough.



  • @cartman82 said:

    LINQ

    I've honestly never seen LINQ used with its actual syntax. It's confusing anyway, both to C# coders (suddenly, you're using the whole different syntax), and to DB coders (it's similar enough to make you feel comfortable with just writing things as if they were SQL, then suddenly throws you off - like, why does select go at the end?)

    It's always the extension methods, and they're fucking awesome (quite similar to how Powershell handles things, with "piping" a set of objects through different where and select filters). The only peeve of mine is that the lambdas aren't always obvious.

    As for what I dislike - hm... The Func<> syntax seems kind of kludgy:

    http://i.imgur.com/ZkvYviT.png

    Also, maybe the lack of support for "implicit getter, explicit setter / vice-versa" scenario.



  • @Maciejasjmj said:

    As for what I dislike - hm... The Func<> syntax seems kind of kludgy:

    Ugh! Right. Although I can't quite imagine how else would they handle it.

    @Maciejasjmj said:

    Also, maybe the lack of support for "implicit getter, explicit setter / vice-versa" scenario.

    Yes. YES!



  • I'm actually a little agitated right now because VS 14 CTP2 doesn't work with Resharper. I feel naked and alone.



  • @coldandtired said:

    int X { get; notify; }

    God this would be convenient.



  • The constructor syntax is really confusing - I learned VB.NET first where you can call the constructor with base.New() (or something like it, haven't done VB.NET in years). and was terribly confused when I tried to do this in Cä.
    Clearly the IL supports this syntax, why not C#?



  • It's silly that unbound events are initially null, so you need to do needlessly complex check to call them safely. Even more so if you want it to be thread-safe also.

    It should have been trivial to either:

    a. Initialize events to some dummy handler that does nothing (yeah, a tiny speed penalty, perhaps - doubt it would matter anywhere at all).

    b. Add a little syntactic sugar to the event call to do the null check automatically and properly.

    Note that you cannot actually use the null-check for any useful purpose anyway, as once-bound and unbound events may or may not be null AFAIK.



  • @cartman82 said:

    Non-generic collections. I know this was fixed in .NET v2.0, but really... WTF were they thinking?

    .NET didn't initially support Generics at all. After the CLR supported Generics with .NET 2 the Framework got the Generic collections. The old collections are kept around for compatibility. They ought to be marked Obsolete though- too many times new code get's written using ArrayList.



  • CTP3 is out now. Haven't tried it myself yet though.



  • Gasp! I'll check it out in a few hours



  • @BC_Programmer said:

    .NET didn't initially support Generics at all. After the CLR supported Generics with .NET 2 the Framework got the Generic collections. The old collections are kept around for compatibility. They ought to be marked Obsolete though- too many times new code get's written using ArrayList.

    Sadly a codebase I've taken over from a coworker takes this a step further. When I complained about ArrayList they moved to List<object>.

    Edit: niiiice, just using straight < without HTML encoding it myself makes it treated as a tag?



  • Oh yes, yes it does. Except in most cases the tag will be permitted as-is because Discourse doesn't block that, it only locks out certain tags that are dangerous and just permits everything else. Though <object> was a tag at one time anyway...



  • ArrayList and Hashtable are super-handy when dealing with JSON.



  • @blakeyrat said:

    ArrayList and Hashtable are super-handy when dealing with JSON.

    Unfortunately most of the C# I write that deals with web services has to deal with SOAP rather than JSON. I quite like using dynamic when dealing with JSON though.


  • Garbage Person

    @jpa said:

    It's silly that unbound events are initially null
    That's not silly. It's evil and I hate it with the burning fire of a thousand suns.


  • Fake News

    @cartman82 said:

    what are some of the things you HATE (or just dislike) about c#?

    Disclaimer: I'm mainly a Java dev, and hence I am looking in from the outside.

    But there is one thing I always wondered about: why oh why does throw e; reset the stacktrace of that existing exception? Couldn't they have made it more explicit by calling an extra function on that exception if you really wanted to reset it?



  • @JBert said:

    Disclaimer: I'm mainly a Java dev, and hence I am looking in from the outside.

    But there is one thing I always wondered about: why oh why does throw e; reset the stacktrace of that existing exception? Couldn't they have made it more explicit by calling an extra function on that exception if you really wanted to reset it?

    Yeah. Seems like some kind of "helpful" feature that isn't helpful at all.


  • FoxDev

    @Weng said:

    That's not silly. It's evil and I hate it with the burning fire of a thousand suns.

    It may be annoying but at least it's easy to solve*.

    public delegate void EvtHandlerFunc(object sender, object data);
    public event EvtHandlerFunc MyEvent = (a,b) => {};
    

    *) Going by memory here. this might not be the exact right syntax. if it isn't right then its close

    Edit: forgot to quote source quote.


  • Garbage Person

    @JBert said:

    But there is one thing I always wondered about: why oh why does throw e; reset the stacktrace of that existing exception? Couldn't they have made it more explicit by calling an extra function on that exception if you really wanted to reset it?
    To rethrow without resetting the trace, just 'throw'.


  • Discourse touched me in a no-no place

    @Minimaul said:

    Edit: niiiice, just using straight < without HTML encoding it myself makes it treated as a tag?

    Just put it in backticks: List<object> (That's a true markdownism.)


  • Fake News

    @Weng said:

    To rethrow without resetting the trace, just 'throw'.

    I am aware of that (as I got to know the "feature" by someone explaining the difference between the two). I was asking "Why?".



  • @JBert said:

    But there is one thing I always wondered about: why oh why does throw e; reset the stacktrace of that existing exception? Couldn't they have made it more explicit by calling an extra function on that exception if you really wanted to reset it?

    If you don’t reset it the stacktrace will show the point where the exception was constructed, not where it was thrown.
    A program could do something like this:

    Exception exc = new Exception("Some exception");
    
    // Some code
    if (condition1) {
    throw exc;
    }
    
    // Some more code
    if (condition2) {
    throw exc;
    }
    

    Yes, if someone actually does this, it’s a WTF in itself. But still, it won‘t help you find at which point the exception was thrown.


  • Discourse touched me in a no-no place

    @HdS said:


    Wut?
    @HdS said:
    The constructor syntax is really confusing - I learned VB.NET first where you can call the constructor with base.New() (or something like it, haven't done VB.NET in years). and was terribly confused when I tried to do this in Cä.
    Clearly the IL supports this syntax, why not C#?

    I think it's not so much that the IL supports any syntax at all, but rather that that's how you write it in VB.NET. In C#, you put the magic new keyword first.

    Does VB let you use a class name in a variable? That would be neat, and would be entirely doable with some clever coding. (Doubly neat would be if it allowed you to override the New “method”, but I really doubt they do that. That's a level of class customizability that the .NET family doesn't seem to have gone to.)



  • @VinDuv said:

    If you don’t reset it the stacktrace will show the point where the exception was constructed, not where it was thrown.
    A program could do something like this:
    Exception exc = new Exception("Some exception");

    // Some code
    if (condition1) {
    throw exc;
    }

    // Some more code
    if (condition2) {
    throw exc;
    }

    Yes, if someone actually does this, it’s a WTF in itself. But still, it won‘t help you find at which point the exception was thrown.


    Huh????

    You realize that every exception ever thrown can be tracked back to code that matches that pattern, right? Usually the code is in the framework, but that doesn't mean it's invalid in your code.

    Example of where a business developer might do it:

    Exception exc = new InvalidArgumentException("Salary cannot be negative");
    
    // Some code
    if (value < 0) {
      throw exc;
    }
    


  • @Maciejasjmj said:

    As for what I dislike - hm... The Func<> syntax seems kind of kludgy:

    That's just an optimization. If performance wasn't an issue, they'd have just made it a paramarray and the method signature would be very neat. However, they made overloads for all numbers of arguments up to 16 so the most common cases would execute faster.


  • FoxDev

    @Jaime said:

    That's just an optimization. If performance wasn't an issue, they'd have just made it a paramarray and the method signature would be very neat. However, they made overloads for all numbers of arguments up to 16 so the most common cases would execute faster.

    I would have much preferred that they stop as something between 5 and 8 parameters and not allowed the paramarray at all.

    Edit: @Bort pointed out that ParamArray wasn't infact supported. Updating to reflect this.

    In my book using more than 5 parameters to a method is a warning sign and more than 8 is almost certainly an indication that you are doing things wrong.

    If you need more than 8 parameters you should probably create an options object (for a group of options to control behavior of method) or a data object (for a group of parameters that represent an object that the method operates on)

    If neither works... well.. may the goddess preserve you.



  • @Jaime said:

    If performance wasn't an issue, they'd have just made it a paramarray and the method signature would be very neat. However, they made overloads for all numbers of arguments up to 16 so the most common cases would execute faster.

    How could it have retained a check-able type signature if it were a ParamArray? C# doesn't have dependent types.



  • Styling, but

    VariablesAndMethodsBeginWithCapitalLetters

    insteadOfNormalCamelCase

    Also last time I checked, .NET documentation looks horrible compared to generated javadoc pages



  • @Bort said:

    How could it have retained a check-able type signature if it were a ParamArray? C# doesn't have dependent types.

    Good catch. I'm used to that style in String.Format and a bunch of others. You are correct that Func is a different animal and they aren't just overloads.



  • @Jaime said:

    Huh????

    You realize that every exception ever thrown can be tracked back to code that matches that pattern, right? Usually the code is in the framework, but that doesn't mean it's invalid in your code.

    Example of where a business developer might do it:

    Exception exc = new InvalidArgumentException("Salary cannot be negative");
    
    // Some code
    if (value &lt; 0) {
      throw exc;
    }
    ```</blockquote>
    
    Maybe I'm missing something but why would anything ever be written in that insane way rather than, say,
    

    if (salary < 0) {
    throw new SalaryCantBeNegativeException("what are you thinking?");
    }



  • Sure, that style is much better, but I was copying the style of the post I was responding to. I was under the impression that the complaint was about creating an exception rather than about instantiating objects that may or may not be used.



  • @Jaime said:

    Sure, that style is much better, but I was copying the style of the post I was responding to. I was under the impression that the complaint was about creating an exception rather than about instantiating objects that may or may not be used.

    My point was that:

    • creating exceptions in advance is a bad programming style in general, but is nevertheless allowed by the language so it’s possible to stumble on some code doing this
    • On Java and probably other languages, the stack trace is filled when the object is created so creating exceptions way before throwing them will complicate debugging
    • C# avoids this problem by filling the stack trace when the object is thrown.


  • @JazzyJosh said:

    VariablesAndMethodsBeginWithCapitalLetters

    insteadOfNormalCamelCase

    Kinda makes sense - capitals for the public-facing fields/classes, camelCase for locals, underscores for private fields. At least I believe that's how ReSharper nags you to name your stuff.


  • ♿ (Parody)

    @Maciejasjmj said:

    Kinda makes sense - capitals for the public-facing fields/classes, camelCase for locals, underscores for private fields. At least I believe that's how ReSharper nags you to name your stuff.

    Why don't they just make your IDE apply a custom heatmap?



  • Underscores for private variables is actually a no-no



  • @VinDuv said:

    On Java and probably other languages, the stack trace is filled when the object is created

    That's the stupidest thing I've ever heard. It's so stupid that I didn't even consider that's what you were addressing.


  • FoxDev

    @Jaime said:

    That's the stupidest thing I've ever heard. It's so stupid that I didn't even consider that's what you were addressing.

    It's horrible, there is an argument in defense of that position. I don't agree with it but the argument goes something like this.

    By setting the stack trace on object creation one can create the exception at the point the error was first encountered and save it to throw later if corrective action was successfully taken failed.

    of course that is BS, if you are going to attempt corrective action then you shouldn't be creating the exception yet because you don't know if it's an error yet!

    [insert 10k word blakey-rant here]

    [edit: "corrrected" the quote. It's still BS, but at least it makes more sense?]


  • Discourse touched me in a no-no place

    @Jaime said:

    That's the stupidest thing I've ever heard. It's so stupid that I didn't even consider that's what you were addressing.

    When should it be constructed then? When the throw happens? That would cause problems with rethrowing, a pattern that's unfortunately necessary when you want to let a subclass B of exception type A through while trapping all other A instances…


  • ♿ (Parody)

    What I love about the nuances of exception handling is that so many people have such strong and conflicting ideas.


  • FoxDev

    @dkf said:

    When should it be constructed then? When the throw happens? [snip]

    that's exactly when it should happen. if you are explicitly throwing the exception (throw ex;) then the stack trace should start there. C# gives you the option to throw without resetting the stack trace. (throw;) That way you can allow passthrough.

    this syntax allows you to preconstruct your exceptions (for whatever crazy reason) and throw them later, having the expected behavior in the stack trace. You can also, only in the context of an exception handler, rethrow an exception and leave the stack trace intact.


  • Discourse touched me in a no-no place

    I suspect it is because it's all a bunch of imperfect trade-offs and some people find those very hard to stomach. It always amuses me how some people in this business can't cope with even the slightest, most necessary compromise, or the necessity of making the normal case work well when there's some complex edge case that isn't great by some metric they're not telling anyone else.

    Up until I actually have to deal with these people. Then I hate it.



  • C# has been my favourite language I have used professionally, but still...

    C# has null.

    'nuff said.


  • FoxDev

    @Bort said:

    C# has null.

    +1 on that one

    Tony Hoare, arguably the inventor of null, discusses the creation of null and why it was a mistake on infoQ

    Raymond Chen, or possibly coding horror, or maybe eric lippert (really not sure and can't find link) did a nice writeup about the creation of null and why it's just a bad thing in general. if anyone can find the link i'd be most grateful as my google-fu has failed me today.



  • If you want a rational argument for why the stack trace should start at "throw"...

    Code for discussion:

    Exception e = new Exception();
    if (a) throw e;
    if (b) throw e;  
    

    If the stack trace starts at "throw", then you know the exact code path of the exception you just caught. You know if condition a or condition b was the cause of it.

    If the stack trace starts at "new", then you have no idea what actually happened.

    This is not a case of "both are equally right, they're just different". If "throw" doesn't anchor the stack trace, it might as well be "return".


Log in to reply