C# hate



  • IMHO, an exception that's hasn't been thrown shouldn't have a stack trace. The stack trace answers the question "where was this thrown", not "where was this created".



  • I think that exact thing is coming in the next version.


  • FoxDev

    Isn't that already covered by this syntax?

    new Rectangle {
        PositionX = x,
        PositionY = y,
        Width = width,
        Height = height
    };
    

    Or are you thinking about properties with private/protected setters?



  • You can already do this:

    var rectangle = new Rectangle{ PositionX = x, PositionY = y, ... };
    

    Edit: ninja'd!



  • There's a difference between "creating an instance with these property values" and "defining a constructor that maps parameters to properties". One is a one-time solution to writing one bit of code and the other helps define an object that can only be created in a valid state. They address different problems.



  • Private setters would be one use for it.
    I'm more concerned about making certain properties required though, so some idiot (not saying I've ever done this before, no of course not...) doesn't shoot himself in the foot by only setting 3 of those.

    new Rectangle {
        PositionX = x,
        PositionY = y,
        Height = height
    };

  • Fake News

    @Jaime said:

    IMHO, an exception that's hasn't been thrown shouldn't have a stack trace. The stack trace answers the question "where was this thrown", not "where was this created".
    I guess I'd reply with a Zen koan:

    If an exception is created it in a forest and it is never thrown, does it have a stacktrace?

    Java's design might be stunted, but C# seems to violate the principle of least surprise.

    So after seeing the whole discussion, I guess I should rephrase my original question like this: "why does C# allow to set the stacktrace twice by using throw e;?"



  • @JBert said:

    seeing the whole discussion, I guess I should rephrase my original question like this: "why does C# allow to set the stacktrace twice by using throw e;?"

    Who cares? At the point between when an exception is created and it is thrown, it isn't an interesting object and its state is meaningless. The purpose of an exception is to be caught and inspected. The catcher expects to see information about the exception that was thrown including where it was thrown from. No one ever thinks "where was this exception object created?". Everyone thinks "where was this thrown from?". So, the principal of least surprise dictates that the stack trace should represent what people intuitively think it should represent, rather than some implementation detail.


  • Fake News

    I'll add some more context.

    Suppose you create an exception, then throw it. If you catch it, log it and then re-throw it, why would the language be so helpful to reset the stacktrace again instead of leaving it alone, all because you wrote catch (Exception e) { throw e; } instead of catch { throw; }? Suddenly you lost all that information...

    If there really is a use case for resetting your stacktrace in a catch block, they could have introduced something like throw e.clone(); or something.



  • @JBert said:

    ... why would the language be so helpful to reset the stacktrace again instead of leaving it alone ...

    Because that's what "throw e;" does. I know you don't like it, but it is logically consistent with all of the other exception behavior of C#. If it didn't reset the stack trace, it would be surprising given that the first throw established a stack trace.

    You didn't "lose information", you "ran a statement that resets the stack trace", which then reset the stack trace. That's like complaining that "x=4;" loses information.

    If "throw e.clone();" worked, it would require throw to have special behavior when throwing a clone. What would happen if you re-threw that clone? Would it be identified as a clone and have the stack trace remain the same, or would it be identified as not being cloned in this context? That's a rabbit hole that should not be explored.


  • ♿ (Parody)

    Aren't you supposed to wrap that rethrow in a new (higher level) exception? I'm no exception handling guru, but the real question is why you're catching it at all if you're just going to re-throw it.



  • @boomzilla said:

    Aren't you supposed to wrap that rethrow in a new (higher level) exception? I'm no exception handling guru, but the real question is why you're catching it at all if you're just going to re-throw it.

    Yes you are.

    There are certain conditions where you want to "catch and release". For example, you might be interested in handling some HTTP status codes but not others. (Both 404 and 500 are thrown as Exceptions. Stupidly. You probably want to handle a 404, but probably can't do anything about a 500.) So you have to dive into the HttpException, check the status, then either handle or rethrow.

    If you're not in that kind of edge-case, your choices are throw a new Exception with its InnerException set to the original, or use "throw;" to throw the current exception unmodified.


  • FoxDev

    @Jaime said:

    So, the principal of least surprise dictates that the stack trace should represent what people intuitively think it should represent, rather than some implementation detail.

    Which is fine if you have access to the source code, and can trace the error and fix it. But say you're using a proprietary library, which throws an exception deep within. If you leave the stack trace alone, you've now exposed internal implementation details, violating encapsulation. So C# (or more accurately, .NET) gives developers the option to catch and rethrow with a new stacktrace to prevent this from happening.



  • @RaceProUK said:

    Which is fine if you have access to the source code, and can trace the error and fix it. But say you're using a proprietary library, which throws an exception deep within. If you leave the stack trace alone, you've now exposed internal implementation details, violating encapsulation. So C# (or more accurately, .NET) gives developers the option to catch and rethrow with a new stacktrace to prevent this from happening.

    Uhhhh..... thanks for agreeing with me......???...??..??


  • Discourse touched me in a no-no place

    @abarker said:

    There is absolutely nothing in the C# which prevents the use of an underscore in a variable name.

    Confusion possibly arises from a restriction in C:

    In addition to the names documented in this manual, reserved names include all external identifiers (global functions and variables) that begin with an underscore (‘_’) and all identifiers regardless of use that begin with either two underscores or an underscore followed by a capital letter are reserved names. This is so that the library and header files can define functions, variables, and macros for internal purposes without risk of conflict with names in user programs.

    (Also carried over to C++)



  • Sounds like the SQL syntax is for human readability, the LINQ syntax is for those with CDO.


  • Fake News

    @RaceProUK said:

    Which is fine if you have access to the source code, and can trace the error and fix it. But say you're using a proprietary library, which throws an exception deep within. If you leave the stack trace alone, you've now exposed internal implementation details, violating encapsulation. So C# (or more accurately, .NET) gives developers the option to catch and rethrow with a new stacktrace to prevent this from happening.

    Not sure if you want to say something ironic or if you are just trolling.

    In any case, the bad ideas thread is 🔄 ↘ ↕ way, and the evil ideas thread is then on your left.


  • FoxDev

    @JBert said:

    Not sure if you want to say something ironic or if you are just trolling.

    In any case, the bad ideas thread is 🔄 ↘ ↕ way, and the evil ideas thread is then on your left.

    I wasn't aware encapsulation was such a bad idea.



  • @RaceProUK said:

    I wasn't aware encapsulation was such a bad idea.

    Your post might be sane in a world where .NET Reflector doesn't exist.



  • Regardless, it's a general comment on encapsulation in the middle of a discussion on C# syntax. It also quoted my post using "But...", but then didn't go on to say anything that disagreed with what the quoted post said.



  • @RaceProUK said:

    I wasn't aware encapsulation was such a bad idea.

    "- Okay, so you're saying you found a bug in our library. What does the stack trace say?

    • Uuuuh... <internal implementation details>?"

  • FoxDev

    @Maciejasjmj said:

    "- Okay, so you're saying you found a bug in our library. What does the stack trace say?

    • Uuuuh... <internal implementation details>?"

    Because library developers are banned from using debuggers?


  • @RaceProUK said:

    Because library developers are banned from using debuggers?

    As you should already know after three months of using this forum, the user is the best debugger.

    Honestly though, obtaining a stack trace might save you a bit of repro, and what's the point of encapsulation in this particular case anyway? When debugging, more information is generally better than less information - even if you don't have the use for it, somebody more knowledgeable might.



  • @RaceProUK said:

    I wasn't aware encapsulation was such a bad idea.

    You might be guilty of cargo-cult encapsulation.

    When we talk about encapsulation - what are we really worried about? Knowing too much?

    The problem I've observed is when we take something that is only temporarily or incidentally true (implementation details) and depend on it being permanently true.

    One can be aware of implementation details for the sake of diagnosis without designing their system to be dependent on those details.


  • Discourse touched me in a no-no place

    reserved name, that's what I was looking for.



  • @cartman82 said:

    creating a new throwaway object

    Generational garbage collection makes allocation of short-lived objects close enough to free that you shouldn't worry about it. You're going to have much more expensive operations elsewhere, whatever your problem domain.




  • BINNED

    @TwelveBaud said:

    new plugin idea:map topic IDs to arrow-emoticon trips and auto-hotlink them

    the bad ideas thread is over there 🔀 ↩



  • @VinDuv said:

    creating exceptions in advance is a bad programming style in general

    How about methods with a return type of Exception? And when they're called, the return value is checked to see whether anything went wrong.



  • @kilroo said:

    How about methods with a return type of Exception? And when they're called, the return value is checked to see whether anything went wrong.

    Either use exception handling or don't. Your idea won't even work properly in C#. I just checked and the StackTrace property of an exception is null until it's thrown.


  • Fake News

    @kilroo said:

    How about methods with a return type of Exception? And when they're called, the return value is checked to see whether anything went wrong.
    There's a language for that.



  • Stop channelling @ben_lubar


  • Fake News

    Oops, I forgot an " ⁉ " there.

    Seriously, I could never propose go with a straight face.



  • Wasn't sure if trolling 😆



  • @kilroo said:

    How about methods with a return type of Exception? And when they're called, the return value is checked to see whether anything went wrong.

    So, basically, functions which can only fail in various spectacular ways and never return an actual result of processing.

    I like the idea.



  • @Maciejasjmj said:

    So, basically, functions which can only fail in various spectacular ways and never return an actual result of processing.

    I like the idea.

    I don't. I inherited it. I keep looking for time to rewrite those codebases...I got rid of one of them by converting it to Powershell.



  • public Exception sum(int x, int y)
    {
        if (x < 0 || y < 0)
        {
            return new InvalidOperationException("Use subtraction instead");
        }
        if (x > 1000 || y > 1000)
        {
            return new ArgumentOutOfRangeException("One of the values is too big to handle");
        }
        if (x != x || y != y)
        {
            return new Threading.ThreadStateException("Values have been modified by a different thread");
        }
        if (x == 42)
        {
            return new NotImplementedException("This number is not currently supported");
        }
        return new Exception(); //should never happen
    }
    


  • I picture it as a void with a bad design.



  • Throw and immediately catch in the method that returns Exceptions, problem solved...

    I'll show myself to the Bad Ideas thread



  • You would use the out keyword in the parameters list shudder



  • public Exception sum(int x, int y)
    {
        if (x < 0 || y < 0)
        {
            return new InvalidOperationException("You're doing it wrong.");
        }
        if (x > 1000 || y > 1000)
        {
            return new ArgumentOutOfRangeException("This operation is rate-limited. Please try again in 23 hours 59 minutes.");
        }
        if (x != x || y != y)
        {
            return new Threading.ThreadStateException("You clicked the like button twice and encountered a race condition");
        }
        if (x == 42)
        {
            return new NotImplementedException("Not priority for V1.0");
        }
        return new Exception(); //should never happen
    }
    

    JTFY



  • @kilroo said:

    How about methods with a return type of Exception? And when they're called, the return value is checked to see whether anything went wrong.

    That's about the way most of the Windows API functions work. The actual payload is returned in a structure whose pointer is passed as argument to the function, and the function returns an integer that is 0 in case of success and non-0 otherwise. (Which number means which error condition, see the documentation of that function.)



  • Isn't that actually standard fare for C APIs, since the language literally has no other way to signal errors (well, except maybe abort())?



  • @Jaime @bp_ @JBert @boomzilla @blakeyrat

    The way C# (and the Intermediary Language) is designed to re-throw exceptions is (besides throw without arguments), like

    catch (ex as OriginalTypeOfExpression) {
      throw new MyOwnFavoriteTypeOfException(
        "Something went wrong in a method my nice flawless method called. \n\
          And to demonstrate it's not my fault, you'll find the details in the InnerException property of this exception.",
        ex)
    }
    


  • Yes; and I think that's where the Windows API took it from.

    I have very little actual (RL) experiences with C, but a bit with the Windows API, so this is the first thing that came to my mind in this context.

    BTW, I believe this is the reason why shells use 0 for true and non-0 for false.


  • Fake News

    That's what I would call exception wrapping and not a re-throw. Also, in your example you don't need the stack-resetting side-effects of throw ex because creation and throwing coincide.

    I still feel that side-effect should have been reserved for a specialized keyword.



  • Oh man, I've spent a year restlessly researching how to properly rethrow exceptions, and finally I got the answer! And I'm sure all those folks you @mentioned couldn't sleep at night becauseof it too!


    Filed under: do we have a Necro of the Year award?



  • Okay, wrapping is probably a better word for it.



  • @JBert said:

    I still feel that side-effect should have been reserved for a specialized keyword.

    It's a moot point anyway, since there's literally zero reasons to a) not preserve the whole stacktrace and b) create an exception object outside the throw. Even if you have a lot of information to pass and need to fill it gradually, just extract the whole mess to a separate class and make an object of that class a property of your exception.


  • Discourse touched me in a no-no place

    @Maciejasjmj said:

    It's a moot point anyway, since there's literally zero reasons to a) not preserve the whole stacktrace and b) create an exception object outside the throw.

    I've seen people create an exception elsewhere and just throw it on error, presumably to try to avoid having the exception object creation cause more problems. It always struck me as being a bit of an odd thing to do; non-trivial exception constructors are probably :doing_it_wrong:.


Log in to reply