𝄯 Sharp Regrets: Top 10 Worst C# Features


  • BINNED

    @Gaska said:

    should we really design programming languages to cater idiots?

    There are people here (OK, just one really) who believe that everyone who wants to should be able to program. 🚎



  • @Gaska said:

    should we really design programming languages to cater idiots?

    No, but we should design them to be as simple as possible.



  • @antiquarian said:

    There are people here (OK, just one really) who believe that everyone who wants to should be able to program.

    Well, why shouldn't they?


  • BINNED

    Are we still talking about the idiots or people in general?



  • This is what you wrote:

    There are people here (OK, just one really) who believe that everyone who wants to should be able to program.

    This is what I am replying to:

    There are people here (OK, just one really) who believe that everyone who wants to should be able to program.

    OH HEY THEY ARE THE SAME!


  • BINNED

    I keep forgetting that you're context-impaired. You are aware that "everyone" includes "idiots", right?



  • @antiquarian said:

    You are aware that "everyone" includes "idiots", right?

    Yes, I post on this forum, don't I?


  • Banned

    @PWolff said:

    At large, I agree with your first paragraph, but properties have been there in VBA as long as I know that, and I suppose this is something C# has taken from the BASIC side.

    VBA also has a switch statement (called select) that breaks after the first case it executes completes.

    @PWolff said:

    Although in this case, I think that properties can be really useful, like the background color of a control element which needs some coding to visually reflect the change of the property.

    Personally, I'd like if the repainting didn't happen immediately, but it was delayed until next main loop cycle. I know it's exactly what happens in real life (assuming non-idiotic implementation), but it was just an example to illustrate the drastic semantics change in accessing variables.

    @blakeyrat said:

    No, but we should design them to be as simple as possible.

    Everyone has different idea what it means for something to be simple. Easy to use? Non-complex? How to measure complexity? What it means to be easy to use? And where lies the boundary of "as possible"?

    @blakeyrat said:

    Well, why shouldn't they?

    For the same reason why people who can't tell throttle from brake aren't allowed to drive.



  • @Gaska said:

    Everyone has different idea what it means for something to be simple.

    That's true, and it's a huge problem. Ruby of Rails or WebAPI guys think "simple" means the framework does a million of things behind the scene so you write fewer lines of code. I see that as "complicated" because it involves trusting in blackbox behavior you can't verify and the instant something goes wrong, you're completely lost determining how to debug or fix it.

    Obviously I'm right here.

    @Gaska said:

    For the same reason why people who can't tell throttle from brake aren't allowed to drive.

    Yes they are. At least, here in the US.


  • ♿ (Parody)

    @blakeyrat said:

    Obviously I'm right here.

    Right where? I don't see you anywhere.

    @blakeyrat said:

    I see that as "complicated" because it involves trusting in blackbox behavior you can't verify and the instant something goes wrong, you're completely lost determining how to debug or fix it.

    I both agree and disagree with you. But this is like arguing about which license is more free without having a precise definition of the word free.


  • Banned

    @blakeyrat said:

    I see that as "complicated" because it involves trusting in blackbox behavior you can't verify and the instant something goes wrong, you're completely lost determining how to debug or fix it.

    s/that/properties/ and it could be what I might've said. Except I've got so good at debug-print-debugging at my current company I doubt I would find myself complaining about this.

    @blakeyrat said:

    Obviously I'm right here.

    While obvious is kind of subjective, thought it's impossible to disagree on the definition of "right".

    @blakeyrat said:

    Yes they are. At least, here in the US.

    I'm dubious of any facts coming from blakeymouth. Can anyone else confirm that?


  • Banned

    @boomzilla said:

    But this is like arguing about which license is more free without having a precise definition of the word free.

    Still, imprecise definition isn't as bad as outright wrong one, like that one made up by FSF.

    🍿



  • @Gaska said:

    VBA also has a switch statement (called select) that breaks after the first case it executes completes.

    Select has so many features that switch doesn't that they can barely be called similar.
    @Gaska said:

    Personally, I'd like if the repainting didn't happen immediately, but it was delayed until next main loop cycle.

    It does. Windows has an API call named Invalidate that tells the OS that a region of a window is dirty and Paint should be called on it when it next comes into view. Anyone not using it (or not using something that does it properly for you) is Doing It Wrong©.



  • @blakeyrat said:

    Ruby of Rails or WebAPI guys think "simple" means the framework does a million of things behind the scene so you write fewer lines of code. I see that as "complicated" because it involves trusting in blackbox behavior you can't verify and the instant something goes wrong, you're completely lost determining how to debug or fix it.

    Thesis: "simple" is not the same as "easy" and in the long run, it's better to do things the simplest way rather than what's easiest right now.

    Not really news when you put it that way.



  • I think I'd prefer the phrase "straightforward" over "simple".

    I want code that's straightforward. It might be complicated as shit, but if it's straightforward, at least I can plug in a debugger and trace through it and figure it out.


  • ♿ (Parody)

    @Gaska said:

    Still, imprecise definition isn't as bad as outright wrong one, like that one made up by FSF.


  • Banned

    Just wait.


  • Banned

    @Jaime said:

    Select has so many features that switch doesn't that they can barely be called similar.

    A quick read-up on a construct I've never seen in a language I've never used left me thinking that all those "so many features" boil down to ranges. Which in reality isn't that much (kinda like std::shared_ptr isn't a big addition to C++).

    @blakeyrat said:

    I think I'd prefer the phrase "straightforward" over "simple".

    "Straightforward" has the same problem as "simple" - people can never agree what is the most straightforward thing because everyone has different preferences.



  • @Gaska said:

    A quick read-up on a construct I've never seen in a language I've never used left me thinking that all those "so many features" boil down to ranges.

    Select Case True
      Case x = y
        ' Do stuff
      Case a = b
        ' Do other stuff
      Case Else
        ' More stuff
    End Select
    

    C's switch is restrictive enough that the compiler has a lot of capabilities for optimization. VB's Select Case allows arbitrary expressions in Case, which hinders optimization, but adds a ton of flexibility. It also has several range syntaxes as you mentioned.



  • @Gaska said:

    "Straightforward" has the same problem as "simple" - people can never agree what is the most straightforward thing because everyone has different preferences.

    Well then I'll just kick them in the crotch until they agree.



  • Ew, do not want.


  • Banned

    @Jaime said:

    ```
    Select Case True
    Case x = y
    ' Do stuff
    Case a = b
    ' Do other stuff
    Case Else
    ' More stuff
    End Select

    That's so back-asswards it hurts. I don't mean the language feature, but how it was used here.

  • BINNED

    @Gaska said:

    I'm dubious of any facts coming from blakeymouth. Can anyone else confirm that?

    It's a standard blakeyfact. No confirmation needed.

    https://what.thedailywtf.com/t/book-teh-o-cial-discopaedia-abarker-creator-and-prophet-of-the-discopaedia/3866/634?u=antiquarian


  • Banned

    I know. I'm just curious if it's really like that on the other side of the pond.



  • @Gaska said:

    That's so back-asswards it hurts. I don't mean the language feature, but how it was used here.

    I'm no fan of that pattern, it was chosen as an example of something that's miles outside what switch was ever intended to do. VB's Select Case is more a replacement for nested Ifs than switch was ever intended to be.


  • Banned

    I don't see the nested part here. Did you mean chained?



  • Select Case x
      Case 4
        ' Do stuff 1
      Case > 20
        ' Do stuff 2
      Case Else
        ' Do stuff 3
    End Select
    

    ... is the equivalent of ...

    If x = 4 Then
      ' Do stuff 1
    Else
      If x > 20 Then
        ' Do stuff 2
      Else
        ' Do stuff 3
      End If
    End If
    

    Chained ifs are simply nested ifs that don't use curly braces around the inner ifs. It's not different syntax, just a style that improves readability.


  • Banned

    @Jaime said:

    Chained ifs are simply nested ifs that don't use curly braces around the inner ifs. It's not different syntax, just a style that improves readability.

    Yes, chained ifs are just special case of nested ifs, but they're so common and use case is so different from other nested ifs I had to point it out. Also, they're the only kind of nested ifs that map easily to select statement.



  • For chained Ifs, Visual Basic has the keyword ElseIf.

    I think they're handled just as nested Ifs, but they are handled differently by the editor (all ElseIfs are the same indentation level).

    IIRC the documentation says the difference to Select Case is that in Select Case the switch expression is evaluated only once.



  • @dkf said:

    I wonder if you could use LINE to make that code less magic-number ridden.

    Or __COUNT__, if you have a C99-compatible preprocessor (or is it C11? CBA to check...)



  • @PWolff said:

    IIRC the documentation says the difference to Select Case is that in Select Case the switch expression is evaluated only once.

    Different to what? I can't think of a language where there is a similar construct, but the expression is evaluated more than once. In C#, the switch expression is evaluated once at run time. case supports expressions, but they are limited to expression that can be evaluated at compile time.



  • @tar said:

    C99-compatible preprocessor (or is it C11

    Will we have the C2K problem in about 53 years?



  • @Jaime said:

    Different to what?

    The chained if (If cond1 ... Then ... ElseIf cond2 Then ... ElseIf cond3 Then ... Else ... End If).

    If the condN are of the form switchExpr = exprK, which makes the chained ifmost similar to Select Case, switchExpr will be evaluated every single time.

    @Jaime said:

    In C#, the switch expression is evaluated once at run time. case supports expressions, but they are limited to expression that can be evaluated at compile time.

    In Visual Basic, they can be arbitrary expression. Even of a type that isn't convertible to the type of the switch expression – that will just throw an InvalidCastException.



  • @PWolff said:

    In Visual Basic, they can be arbitrary expression. Even of a type that isn't convertible to the type of the switch expression – that will just throw an InvalidCastException.

    Remember, I'm the guy who provided the example that abused this property to the horror of @Gaska and @swayde.



  • @Jaime said:

    Remember, I'm the guy who provided the example that abused this property to the horror of @Gaska and @swayed.

    Point taken


  • Java Dev

    Hm, I didn't know that one. Might play with it for this, as it would enable two yields on one line.

    Will require some macro trickery though - that code does really need the same number twice in the generated C code. I mean, you could do

    #define YIELD(ret) *__yield_pos=__COUNTER__+1; return (ret); case __COUNTER__:
    

    but that's just ugly. And still means you're skipping positions. Using all consecutive numbers might allow for better optimisation by the compiler.



  • @PWolff said:

    If the condN are of the form switchExpr = exprK, which makes the chained ifmost similar to Select Case, switchExpr will be evaluated every single time.

    That's why I did Select Case True and put my expressions in the Cases. In this form, it functions exactly like If.


  • Discourse touched me in a no-no place

    @PleegWat said:

    but that's just ugly.

    The usual way to deal with that is a two-level macro:

    #define YIELD(ret) YIELDLOC(ret, __COUNTER__)
    #define YIELDLOC(ret,ctr) *__yield_pos = (ctr); return (ret); case (ctr):
    

    TIL about __COUNTER__



  • @Jaime said:

    That's why I did Select Case True and put my expressions in the Cases.

    I've done this occasionally in SQL.
    decode(true, cond1, expr1, cond2, expr2, ...)
    That was mainly before I got comfortable with case when cond1 then expr1 when cond2 then expr2 ... end though.

    But there are times when this sort of construction is useful. I've used it in Informatica as well, because

    DECODE(True, 
    condition1, result1,
    condition2, result2,
    condition3, result3,
    ...
    )
    

    is a bit easier to read and maintain than

    IIF(condition1, result1,
       IIF(condition2, result2,
          IIF(condition3, result3, 
    ...
    )...)))
    

    especially when there's a lot of sub-casing going on (i.e. when a lot of the results are actually IIF() statements in their own right).



  • @dkf said:

    TIL about COUNTER

    It's almost as if someone on the standards committee thought: "the use of __LINE__ as a disambiguator in macro output is great and all, but what if I want to have several instances of the same macro on one line?"

    COUNTER?



  • @blakeyrat said:

    @antiquarian said:
    You are aware that "everyone" includes "idiots", right?

    Yes, I post on this forum, don't I?

    That's a misleading example, as in the context of web fora, 'everyone' is a proper subset of 'idiots'.


  • Java Dev

    @dkf said:

    @PleegWat said:
    but that's just ugly.

    The usual way to deal with that is a two-level macro:

    #define YIELD(ret) YIELDLOC(ret, __COUNTER__)
    #define YIELDLOC(ret,ctr) *__yield_pos = (ctr); return (ret); case (ctr):
    

    TIL about __COUNTER__

    Like I said, some trickery. I didn't know about counter either until tar hinted me that way.

    counter seems to start at zero though, so either use -1 for the initial case or add one to the counter when using it here. You can't draw the initial case value from the counter because you need to know it to initialize your state.



  • @ScholRLEA said:

    That's a misleading example, as in the context of web fora, 'everyone' is a proper subset of 'idiots'.

    There are idiots that aren't part of "everyone"?



  • This post is deleted!


  • Good point. Perhaps I should have said that the set of everyone is equal to the set of idiots?

    Come to think of it, that works even outside of the context of Internet fora...


  • Discourse touched me in a no-no place

    @PleegWat said:

    counter seems to start at zero though, so either use -1 for the initial case or add one to the counter when using it here

    Or use it a few times (and throw away the value) and use zero as your initial state.


  • Banned

    I've found yet another small annoyance: you cannot call lambda immediately after declaration. Because apparently lambdas aren't really functions - you must first construct a Func<> or some other function object from them, and only then they're callable.



  • @Gaska said:

    you cannot call lambda immediately after declaration

    Can you give an example, please? I can't imagine what you mean.

    But I found another annoyance: I wonder whether lambdas are treated as a kind of macro sometimes.

    func<int, int> f = (x => x * x);
    var x = f(5);
    

    won't compile because of a name conflict. (The name of a bound variable isn't abstracted, obviously.)

    (f can't be defined generic, either. func<T, T> won't compile.)


  • Banned

    @PWolff said:

    Can you give an example, please?

    (()=>{})()



  • @PWolff said:

    won't compile because of a name conflict. (The name of a bound variable isn't abstracted, obviously.)

    That happens because C# doesn't support variable shadowing.


Log in to reply