𝄯 Sharp Regrets: Top 10 Worst C# Features



  • @Bort said:

    And it would depend on some proprietary UI control set that is out of date and you don't have a license for so the app won't run. (Dealing with this right now).

    You're in the cube next to me, aren't you.



  • @Maciejasjmj said:

    Duh. But there's nothing violating the contract.

    Maybe they expected their developers to be human beings instead of pedantic dickweeds.



  • @Magus said:

    I feel like some of the need is reduced if you're using Rx. I was planning to use it for my game's input, but it freaked out my friend who's working on it with me.

    Some feminist is going to somehow disqualify you from winning any awards anyway, might as well just give up.



  • No, they already lost that war. You can keep fighting for them though, I guess?


  • Discourse touched me in a no-no place

    @blakeyrat said:

    Maybe they expected their developers to be human beings instead of pedantic dickweeds.

    The best developers are all pedantic dickweeds. It's the key skill.



  • @Gaska said:

    Another small C# annoyance: when doing switch, you cannot fall-through, yet you have to write break.

    You can have fall-through in C#. Except it is a :wtf::

    switch(x) {
    case 1:
        doThis();
        goto case 2;
    case 2:
        doThat();
        goto case 3;
    case 3:
        doOtherwise();
        goto case 1; // yesss, yesss, shoot us in the footses
    };
    

    For some reason I thought you can write continue to fall through, but the documentation does not mention that, so I probably saw that in some other language.



  • @Gaska said:

    Another small C# annoyance: when doing switch, you cannot fall-through, yet you have to write break.

    That's just a minor annoyance – they implemented a fixworkaround. They could have implemented a nice fine fall-through, but nooooo, why doing it the simple (and right) way when there is such a wonderful, shiny, a bit more complicated, and broken way to do it:   goto case.

    Yes, it is as broken as it sounds. The following works exactly as intendedexpected:

        static int testGotoCase(int n) {
          Console.WriteLine();
          Console.WriteLine("testGotoCase(" + n.ToString() + ")"); // notify which function we are in
          switch (n) {
              
            case 1:
              Console.WriteLine("switch (n) -> case 1"); // notify where we are
              goto case 4;
              
            case 2:
              Console.WriteLine("switch (n) -> case 2"); // notify where we are
              goto case 3;
              
            case 3:
              Console.WriteLine("switch (n) -> case 3"); // notify where we are
              return 3000;
              
            case 4:
              Console.WriteLine("switch (n) -> case 4"); // notify where we are
              goto case 1;
              
            default:
              Console.WriteLine("switch (n) -> default"); // notify where we are
              goto case 2;
              
          }
        }
    

    Edit: :hanzo:'d by @Bulb

    Edit 2: Nevertheless, the possibility to jump back is a proper :wtf:.


  • Banned

    @Bulb said:

    You can have fall-through in C#.

    Explicit goto isn't fall-through. Learn your terminology, OK?

    @PWolff said:

    They could have implemented a nice fine fall-through

    I would hate them even more if they did it.

    @PWolff said:

    Nevertheless, the possibility to jump back is a proper :wtf:.

    Not really, if you remember that it's plain old goto (as indicated by keyword). At least you cannot goto into middle of a loop...



  • You'd break a lot of things by abusing IEnumerable for cyclic structures. Such as every goddamn extension method available. So by all means, you may fool around with it on your spare time for lulz, but write something like that in production code and I'd shoot you in the face.


  • Discourse touched me in a no-no place

    @donk said:

    write something like that in production code and I'd shoot you in the face

    So you assume that every collection is small enough to conveniently fit into memory?



  • @donk said:

    abusing IEnumerable for cyclic structures

    (Emphasis by me)

    I suppose @donk buttumes a collection must be non-cyclic.


  • Discourse touched me in a no-no place

    @PWolff said:

    I suppose @donk buttumes a collection must be non-cyclic.

    While I fully admit that most of my structures are non-cyclic, there's nothing wrong with them when they're the right tool.



  • Can we agree on foreach being a somewhat misleading name when used for a cyclic loop?



  • Especially if there's a yield in it.

    Which is the case for almost every LINQ extension method - they don't enumerate until you call ToList or similar. So I don't think an infinite collection would break any of them.



  • Infinite is not quite the same as cyclic.

    (In group theory, the additive group of integers is cyclic and infinite, though.)



  • @donk said:

    You'd break a lot of things by abusing IEnumerable for cyclic structures. Such as every goddamn extension method available.

    I'm not even a C# programmer and I know that's not true.
    Here's a list of all the LINQ extension methods by execution type.
    All "deferred streaming" operations work fine on infinite collections, no "deferred non-streaming" methods work, and some "immediate" work (eg First, ElementAt, and Single).



  • Hyperbole etc.

    ToList is an obvious problem, but I also can't imagine Sum, Min, Max, All, Aggregate, Average, Distinct, Count, Last, OrderBy et al working very well at all if the enumeration doesn't terminate.


  • Java Dev

    I've always viewed an enumerable as a mathematical sequence - can be both finite and infinite. The whole 'no duplicate elements, no restart, etc' thing seems mostly related to an enumerable on a collection in memory.


  • Discourse touched me in a no-no place

    Many of those operations can conceptually have analogues on infinite streams (e.g., a running average as a stream instead of the average of the whole stream or the tail of a stream past a certain point as a stream instead of the last element). But you have to think in terms of infinite sequences. If you start out by using the mental model “this came from an array somewhere in memory” then you'll go wrong.



  • Certainly, but that's not the issue here, at least not to me. My gripe is with the hypothesized IEnumerable abuse breaking stuff one may reasonably expect to work, which goes against the principle of least surprise and as such would be a very bad idea in a public library or API of some sort. Or in short: don't do it (unless you're just fooling around).



  • @donk said:

    I also can't imagine Sum, Min, Max, All, Aggregate, Average, Distinct, Count, Last, OrderBy et al working very well at all if the enumeration doesn't terminate.

    Most of those are immediate operations which I already said may not work.
    OrderBy never works as it's not streamed, and Distinct is an oddity which works fine for infinite lists but not cyclic lists.



  • @Salamander said:

    Distinct is an oddity which works fine for infinite lists

    Well, I have a very old machine here, with only 4 GiByte memory. foreach (int i in System.Linq.Enumerable.Range(0, int.MaxValue).Distinct()) tends to get a bit slow after a few dozen million loops, and throws an OutOfMemoryException after less than 200,000,000 loops.



  • Yes, to work out if an element has been seen before, it needs to keep track of the elements it has already seen. What's your point?
    Its runs, returns the output that is expected, and will still terminate the moment you stop reading it, either by breaking out of the loop or by using Take.
    It only fails when you don't have the physical memory needed to run the code, but that's pretty much the same scenario for all programs. Including that snippet you wrote, which isn't even an infinite enumerable, just a large one.


  • Java Dev

    I would be very careful with Distinct on non-terminating enumerables. You're unlikely to know on the consuming end when to stop, and apart from the memory problem there may be a finite set of input elements, not just in cyclic lists, but also for example in an enumerable that returns (pseudo-)random values in a certain range.



  • Psuedo-random is still just a cyclic list though.
    Pretty much the only time you can get an non-cyclic infinite list of finite values is when you throw in a pure random number generator into the mix, at which point you're doing something so extremely wrong nothing will save whatever monstrosity was just coded.
    That being said, needing to use distinct on an infinite list with infinite values means you're probably doing something wrong, but it's still a valid operation.



  • @Gaska said:

    Another small C# annoyance: when doing switch, you cannot fall-through, yet you have to write break.

    I don't know, I've seen/had my share of bugs in several languages after forgetting to add a specific break.

    Now, the goto thing already mentioned gives me the shivers. Imagine the exploitability by some 1337 developer playing around with those goto's. For example:

    switch(x) {
    case 1:
        doThis();
        goto case 2;
    case 2:
        doThat();
        goto case 1; // infinite loop!
    case 3:
        doOtherwise();
        goto case 1; // yesss, yesss, shoot us in the footses
    };
    

  • Discourse touched me in a no-no place

    @Salamander said:

    Pretty much the only time you can get an non-cyclic infinite list of finite values is when you throw in a pure random number generator into the mix, at which point you're doing something so extremely wrong nothing will save whatever monstrosity was just coded.

    There are other ways too, such as a sequence of two numbers (e.g., 0 and 1) where there is a monotonically-increasing interval between each 1 in the stream of 0 values. It's an infinite non-repeating sequence with only two member values and a simple generating rule.

         1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0 ,0 ,0 1, …



  • I stand corrected, never thought of that one


  • Discourse touched me in a no-no place

    @Eldelshell said:

    Now, the goto thing already mentioned gives me the shivers. Imagine the exploitability by some 1337 developer playing around with those goto's.

    How else are you going to implement a true state machine? Unfortunately, not all of them are simple to decompose into structured programs (or at least not without increasing the :wtf: factor in other ways).


  • Java Dev

    Just confirmed this actually works (my code was compiling):

    #include <stdio.h>
    
    #define YIELD_START(pos) switch(pos) case 0:
    #define YIELD(pos,i,ret) (pos)=(i); return (ret); case (i):
    
    struct status {
        int pos;
        int i;
    };
    
    int generate(struct status *s)
    {
        YIELD_START(s->pos)
        {
    	for( s->i = 0 ; s->i < 10 ; s->i++ )
    	{
    	    YIELD(s->pos, 1, s->i);
    	    YIELD(s->pos, 2, s->i);
    	}
        }
        return -1;
    }
    
    int main(int argc, char * argv[])
    {
        struct status s;
        int r;
    
        s.pos = 0;
        s.i = 0;
    
        while( (r = generate(&s)) >= 0 )
        {
    	printf( "%d\n", r );
        }
    
        return 0;
    }
    

  • Discourse touched me in a no-no place

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


  • Java Dev

    That should work. Means you can't yield twice on one line, but that seems acceptable. The compiler (or at least gcc) will complain if you use a label twice, but that error ends up nasty with the defines.

    Any ideas to get rid of the pos argument to YIELD without putting the opening brace in YIELD_START? Though you could probably get away with hardcoding s->pos even if you use this multiple times.


  • Java Dev

    In C99 mode you can use these:

    #define YIELD_START(pos) for(int * __pos = &(pos); __pos; __pos = NULL) switch(pos) case 0:
    #define YIELD(ret) *__pos=__LINE__; return (ret); case __LINE__:
    

  • Banned

    @Eldelshell said:

    I don't know, I've seen/had my share of bugs in several languages after forgetting to add a specific break.

    I never said fall-through it a good idea. I just said that if the language doesn't support fall-through, it shouldn't require explicit break after every case - it's just unnecessary clutter. If my car has automatic emergency brake, I shouldn't have to release it every time I get moving after stop.

    @Eldelshell said:

    Now, the goto thing already mentioned gives me the shivers.

    Duh! It's goto after all!

    @dkf said:

    How else are you going to implement a true state machine?

    Have a State interface implemented by every state class, one for each state I want. We don't have to implement '60s design patterns using '60s tools in 2015.



  • @Gaska said:

    Have a State interface implemented by every state class, one for each state I want. We don't have to implement '60s design patterns using '60s tools in 2015.

    This is one of those cases where I can't figure out why people stick with the old ways. You often see performance and bloat as arguments against object oriented programming, but the opposite is true here. A switch statement is implemented as a series of comparisons and jumps, while an interface-based design is implemented as just a pointer in a vtable.


  • Banned

    On most architectures, in most cases, series of comparisons and jumps will still be faster than vtable - remember that the former can be compiled to jump table, and the latter has potential of two cache misses and is harder to optimize for the compiler. But the difference is almost always negligible.


  • Discourse touched me in a no-no place

    @Jaime said:

    A switch statement is implemented as a series of comparisons and jumps

    That's one way they might be done. There are others.



  • If you're going to reuse a language construct like that you can't go changing the basic functionality without some clear warning for people who are used to the other behavior. Belt and suspenders is the only thing that makes sense here.

    If they hadn't forced you to add those breaks, how much more annoying would their (very sensible) decision not to allow fall-through have been to you?


  • Banned

    @Buddy said:

    If you're going to reuse a language construct like that you can't go changing the basic functionality without some clear warning for people who are used to the other behavior.

    Java turned the C world upside down with their reference semantics for variables. string a = "dupa"; string b = a; does totally different thing in Java and C++, yet I never heard a complaint about Java for this particular thing being confusing.

    Of course, there are some people who will try to fall-through in C#, but:

    • they will immediately notice their code is not working as intended;
    • should we really design programming languages to cater idiots?

    @Buddy said:

    If they hadn't forced you to add those breaks, how much more annoying would their (very sensible) decision not to allow fall-through have been to you?

    As I repeated over and over again already - disallowing fall-through was a great decision. My only complaint is about unnecessary verbosity due to break statements which are basically implied there, so the only result is decreased signal-to-noise ratio. Remember that main reason why people hate Java is extreme verbosity.



  • Except that's not the type of mistake an idiot makes, that's the type of mistake anyone could make, coming to c# from another language, or even working on multiple projects in different languages at the same time—sometimes the details slip your mind. And your assertion that they would immediately notice seems a bit naive: if they've already forgotten which semantics they're dealing with once, do you think they're gonna specifically test that behavior? Next do you want them to test that only one branch of their if-else gets taken?

    @Gaska said:

    Remember that main reason why people hate Java is extreme verbosity

    Should we really design programming languages to cater idiots?


  • Banned

    @Buddy said:

    Except that's not the type of mistake an idiot makes

    Writing code in language A that doesn't make sense here because it's how he does things in language B? Yep, definitely not an idiot's way of doing things.

    @Buddy said:

    that's the type of mistake anyone could make, coming to c# from another language

    Only the subset of people who don't know how this particular language feature works. And the solution is RTFM.

    @Buddy said:

    or even working on multiple projects in different languages at the same time

    This excuse makes as much sense as excusing an American driver in Germany for making the right turn on red light and causing accident.

    @Buddy said:

    sometimes the details slip your mind

    The detail that slips my mind most often is to write break after each case. Every time I make case fall-through in C or C++, I never meant to do it. In C#, this results in compilation error. In C# after change I propose, my code would result in correct behavior.

    @Buddy said:

    And your assertion that they would immediately notice seems a bit naive: if they've already forgotten which semantics they're dealing with once, do you think they're gonna specifically test that behavior?

    It's 2015. Anyone who doesn't write unit tests for all code paths is an idiot. Or works in shitty corporation under idiot management, in which case the semantics of switch is the least of his problems.

    @Buddy said:

    Next do you want them to test that only one branch of their if-else gets taken?

    No - I want to them to test their code for every kind of input, exercising every possible code path (except the impossible ones).

    @Buddy said:

    @Gaska said:
    Remember that main reason why people hate Java is extreme verbosity.

    Should we really design programming languages to cater idiots?

    What's idiotic in writing short code?



  • @Gaska said:

    This excuse makes as much sense as excusing an American driver in Germany for making the right turn on red light and causing accident

    It's a mistake anyone could make. It's fucked up that Germany has wacky-ass road rules that don't follow the conventions.

    But if cars could be designed to ensure that every maneuver you made was both intended and legal, and you were here calling that feature pointless noise, I would be calling you a jerk for that just as I am now.

    @Gaska said:

    In C# after change I propose, my code would result in correct behavior.

    Compiler already holds your dick when you piss, now you want it to shake you off before tucking it in.
    @Gaska said:
    What's idiotic in writing short code?

    Redefining existing language constructs to mean the opposite of what they used to is idiotic. C# designers did the best they could with what they had.


  • Banned

    @Buddy said:

    It's a mistake anyone could make.

    An European wouldn't.

    @Buddy said:

    It's fucked up that Germany has wacky-ass road rules that don't follow the conventions.

    What conventions? Nothing in the Vienna Convention says anything about right turns on red light, and I'm not aware of any other international convention regulating traffic laws.

    @Buddy said:

    But if cars could be designed to ensure that every maneuver you made was both intended and legal, and you were here calling that feature pointless noise, I would be calling you a jerk for that just as I am now.

    If I couldn't drive 70km/h on a four-lane road with 50km/h limit, I would bitch about it.

    @Buddy said:

    Compiler already holds your dick when you piss, now you want it to shake you off before tucking it in.

    It's more like compiler fusing my penis until I sit down on the toilet to make sure I don't piss over the floor.

    @Buddy said:

    Redefining existing language constructs to mean the opposite of what they used to is idiotic.

    What is opposite to what, exactly? And who's redefining anything? Additive change isn't redefining, and implicit break is technically speaking an additive change to a language.

    @Buddy said:

    C# designers did the best they could with what they had.

    That's not conclusion, that's premise. A premise I consider false.



  • @Gaska said:

    @Buddy said:
    C# designers did the best they could with what they had.

    That's not conclusion, that's premise. A premise I consider false.

    They did the best they were allowed to do by management.

    This is neither a conclusion nor a premise but a supposition based on my experience (and what many other Internet inhabitants write what is their experience).


  • Banned

    @PWolff said:

    They did the best they were allowed to do by management.

    If shitty feature was ordered by the management, does it make the feature any less shitty?



  • @Gaska said:

    If shitty feature was ordered by the management, does it make the feature any less shitty?

    Of course. Enterprisey is the opposite of shitty.



  • @Gaska said:

    And who's redefining anything? Additive change isn't redefining, and implicit break is technically speaking an additive change to a language

    You have a weird understanding of words.



  • @Gaska said:

    If I couldn't drive 70km/h on a four-lane road with 50km/h limit, I would bitch about it.

    That's because you're a jerk.


  • Banned

    @Buddy said:

    You have a weird understanding of words.

    OK, let's tackle it from another side. You say redefining existing language constructs is bad. Yet C# has already drifted off the established conventions by introducing properties, which is basically method calls disguised as variable accesses. If I say i = 0, I would expect a variable i to have a value 0, not repainting the whole window. Do you think it's OK to introduce such counterintuitive feature? Do you think it's less of redefining existing language constructs than implicit break in switches?

    @Buddy said:

    That's because you're a jerk.

    I don't understand how driving at the speed matching the road condition is being a jerk. If anything, it's the local government that enforce crawl speed on a road that's perfectly appropriate for 100km/h.



  • @Gaska said:

    If I say i = 0, I would expect a variable i to have a value 0, not repainting the whole window. Do you think it's OK to introduce such counterintuitive feature?

    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. (There are some other features/"features" too, like the already mentioned elimination of switch fallthrough.)

    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. That programmers can abuse property methods is a different matter. (See the discussion about foreach and IEnumerable.)

    @Gaska said:

    I don't understand how driving at the speed matching the road condition is being a jerk. If anything, it's the local government that enforce crawl speed on a road that's perfectly appropriate for 100km/h.

    In general, I agree with this, although I tend to obey the authorities (not quite?) as far as feasible. This is another problem I'd like to discuss (am not sure whether a new topic would be advisable).


Log in to reply