The new .NET Exception that should NEVER be thrown

Поділитися
Вставка
  • Опубліковано 10 лют 2025
  • Until the 28th of Nov 2022 use discount code BF2022 for up to 25% off
    at: dometrain.com
    Become a Patreon and get source code access: / nickchapsas
    Hello everybody I'm Nick and in this video I will take a look at a brand new exception that was added in .NET 7, that was designed to never be thrown.
    Don't forget to comment, like and subscribe :)
    Social Media:
    Follow me on GitHub: bit.ly/ChapsasG...
    Follow me on Twitter: bit.ly/ChapsasT...
    Connect on LinkedIn: bit.ly/ChapsasL...
    Keep coding merch: keepcoding.shop
    #csharp #dotnet

КОМЕНТАРІ • 91

  • @capsey_
    @capsey_ 2 роки тому +120

    I usually wrote "WTF???" as unreachable exception message, but then I realized I need to be more professional and that this is an inexcusable behavior, so now I just write "WTH???"

    • @ShadowMasterT
      @ShadowMasterT 2 роки тому +17

      Somewhere out there in a previous company I worked for, I had such an unreachable exception that was previously missing any message, and I was tasked with adding messages wherever possible. On one, which I couldn’t even understand its context, I simply added “¯\_(ツ)_/¯”

    • @ivank6486
      @ivank6486 2 роки тому +6

      Well, "What a Terrible Failure" is a valid log level in android, I see no problem using it in c# code

    • @wojciechwilimowski985
      @wojciechwilimowski985 2 роки тому +2

      I use "say WHAAAAAAT?"

    • @Alguem387
      @Alguem387 2 роки тому +3

      "How did we even get here champ?"

    • @magusware8721
      @magusware8721 2 роки тому +2

      Unreachable exceptions are the best place to hide easter-eggs while maintaining a facade of professionalism

  • @Azcane
    @Azcane 2 роки тому +12

    That's an awesome tip! I've had situations like this where I just threw garbage and would've needed exactly this kind of exception.
    This general solution really makes sense.

  • @robertnull
    @robertnull 2 роки тому +16

    Once I pushed something like this, but it was rejected in review... perhaps because I called it "SchroendingerException" :P

    • @jorsang1
      @jorsang1 2 роки тому +1

      I don't get why it was rejected... The name can't be cooler! 😂😂

    • @robertnull
      @robertnull 2 роки тому

      Ahh, @@jorsang1, so nice to see you here :D

    • @phizc
      @phizc 2 роки тому

      @@jorsang1 Well, it's misspelled.. Schrödinger should be anglicized to Shroedinger, not Shroe -n- dinger 🙂
      edit: strikethrough in the middle of a word didn't work. Ignore the spaces before and after -n-

    • @robertnull
      @robertnull 2 роки тому +1

      @@phizc No coder I've met likes diacritics in class names ;)

  • @nickchapsas
    @nickchapsas  2 роки тому +20

    Second!

  • @neppe4047
    @neppe4047 2 роки тому +4

    The quickly disappearing text added in editing always gets a chuckle out of me for some reason. That was the fastest one I've seen in this channel I think. I was watching in 2x playback speed and would've probably missed it if I had blinked at the wrong time lol.

  • @brianm1864
    @brianm1864 2 роки тому +62

    Good thing Nick isn't working on .NET 7. Otherwise it would be the "WhatTheHellHappenedException" class!

  • @PublicVoidFoo
    @PublicVoidFoo 2 роки тому +32

    Should I stay or should I throw now?
    If I throw, there will be trouble
    And if I stay it will be `double`

    • @zabustifu
      @zabustifu 2 роки тому +1

      The elusive TeamRocketException.

    • @EdKolis
      @EdKolis 2 роки тому +1

      @@zabustifu To protect your code from devastation
      To unite all algorithms within our nation
      To denounce the evils of OOP
      To extend our reach to RAM memory!

  • @liquidcode1704
    @liquidcode1704 2 роки тому +5

    "AggregateException of all tasks was null. What the hell" actually sounds pretty professional to me 🙃

  • @AvenDonn
    @AvenDonn 2 роки тому +1

    I've received unreachable exceptions because there was some funky stuff with marshalling and pinvoke

  • @ekeke7125
    @ekeke7125 2 роки тому +21

    For enums in switch expressions (or sometimes for switches in general) I always use NotImplementedException which feels more correct than UnreachableException.
    But UnreachableException is definitely useful in other cases.

    • @nickchapsas
      @nickchapsas  2 роки тому +20

      I personally disagree. NotImplemented implies that there might be an implementation coming. Unreachable indicates state that should never happen under normal circumstances

    • @ekeke7125
      @ekeke7125 2 роки тому +9

      @@nickchapsas Does it? I never thought of it this way. Not implemented for me always meant something like: hey, sorry, this exception is happening because what is happening in the code is not implemented or you forgot to implement it, especially in case of enums, where someone forgot to implement a case after adding a new enum field.

    • @Tsunami14
      @Tsunami14 2 роки тому +2

      Same...though it's really annoying that after all this time, we still don't have a built in exception type for this error case. (e.g. EnumOutOfRangeException?) It's such a common thing to check for that it's always puzzled me on how there's not a matching exception available.
      There's InvalidEnumArgumentException, but this one doesn't sit right with me because of the namespace (System.ComponentModel).

    • @iliaxj
      @iliaxj 2 роки тому +8

      I go for NotSupportedException, as that's a bit stronger than NotImplementedException. I'm saying that it doesn't exist, and won't forseeably exist.

    • @nickchapsas
      @nickchapsas  2 роки тому +4

      NotSupported would be better than NotImplemented but I still thing it doesn’t convey the truth about the situation.

  • @mabakay
    @mabakay 2 роки тому +1

    20 years ago we called it assertions ;-)

  • @DOHandDOH
    @DOHandDOH 2 роки тому +1

    I would love to hear about your exception alerting system and app monitoring in general.

  • @rikschaaf
    @rikschaaf 2 роки тому +2

    Rather than introducing an unreachable exception they could have just... you know... made those locations ACTUALLY unreachable. For instance, have compile-time checks if switch expressions are exhaustive for enums and prevent assignments to enums that fall outside of the range of acceptable values.

  • @mohamedchaabouni4477
    @mohamedchaabouni4477 2 роки тому +2

    the hell what is happening here but better 😂love this one

  • @krzysztofzabawa2212
    @krzysztofzabawa2212 2 роки тому +2

    This is one of the things I love about TypeScript compared to C#. It can do the exhaustiveness check (as part of code flow analysis) and catch this in compile time rather than run time. Which is a HUGE thing :)

    • @kuroikenjin7652
      @kuroikenjin7652 2 роки тому +2

      Same with Rust... and hopefully other modern languages.

    • @maskettaman1488
      @maskettaman1488 2 роки тому +1

      The Enum case here isn't really something that can be caught by static analysis, mostly because you can define new Enum values at run time. PremiumStatus diamond = (PremiumStatus)5; is totally valid code that might be run at runtime. Enabling the new-ish Nullable Reference Types analysis should cover the Task example as well

    • @krzysztofzabawa2212
      @krzysztofzabawa2212 2 роки тому

      @@maskettaman1488 True for enums and casting, indeed. However if you string literals (which can serve pretty much the same purpose in many cases), static analysis is possible :)

  • @petrusion2827
    @petrusion2827 2 роки тому +1

    What is the difference between UnreachableException, Debug.Assert, and Trace.Assert?
    I never really got too deep into handling unexpected state, but I thought it was Debug.Assert and Trace.Assert that exist to protect against (or rather notify of) unexpected state. So when would I use UnreachableException over those?

    • @AvenDonn
      @AvenDonn 2 роки тому +1

      Exceptions are exceptions. You already know why they're different.
      Debug.Assert should only run if your code is compiled in Debug. Not sure about Trace but I believe Trace is also defined on Release.
      Neither are used to protect against unexpected states

    • @billy65bob
      @billy65bob 2 роки тому +1

      I've never used Trace, but you can think of Asserts as nags that don't interrupt execution, and exceptions as errors that do.

  • @HenrykBudzinski
    @HenrykBudzinski 2 роки тому

    6:32 "What the hell" 🤣

  • @1kuch3n
    @1kuch3n 2 роки тому

    Which IDE are you using?

  • @jongeduard
    @jongeduard 2 роки тому +1

    Interesting and might be useful, when not misused for cases where normal exceptions should be thrown. I was almost going to reply that we already have Assertions for this and things like that, but combining those is a great solution, so that indeed you'll have a fallback for Release build situations indeed.
    I would also like to add that about the ThrowSwitchExpressionException that I checked the IL on Sharplab and that it's simply inside your own compiled code, in an autogenerated default switch arm by the compiler.

  • @sinan720
    @sinan720 2 роки тому +2

    imo, casting an int to enum should only be possible in unsafe code blocks, just like the pointer operations

    • @phizc
      @phizc 2 роки тому +1

      It's not unsafe though. It won't corrupt memory. I could get behind allowing checked blocks, like e.g. when casting int to uint and it's an error if the sign bit is set.
      e.g.
      var enumValue = checked((MyEnum)intValue);
      // this does nothing if MyEnum is based on int. It does throw if MyEnum is based on uint or byte etc. and intValue is negative.
      The default behavior is *checked* on compile, and *unchecked* during runtime for e.g. casts between int and uint, but it's configurable.
      And I definitely wouldn't _require_ the casting to be checked. That would break thousands of code bases for little to no gain.

    • @sinan720
      @sinan720 2 роки тому

      @@phizc
      Yes i agree with you about the checked block, but i still think it shouldnt be possible to cast an int to a non existing enum value.
      This is how i imagined it should work:
      // works if value exists,
      // else throws InvalidCastException
      var enumValue = (MyEnum)intValue;
      // works if value exists,
      // else returns null
      var enumValue = intValue as MyEnum;
      unsafe
      {
      // works every time
      var enumValue = *(MyEnum*)&intValue
      }
      Obviously this would break existing code, so yeah, it will remain just an idea

  • @JustinMinnaar
    @JustinMinnaar 2 роки тому +1

    I've usually thrown the "NotImplementedException" as this should never happen and it means my code is not written properly. I'll keep this in mind for the future.

    • @nickchapsas
      @nickchapsas  2 роки тому +3

      NotImplementedException is defined as "The exception that is thrown when a requested method or operation is not implemented.". It implies that it’s not implemented, not that it’s a state that should never be reached.

  • @AnotherFancyUser
    @AnotherFancyUser 2 роки тому

    You are right, this exception should never be thrown, you can't account for all the thing that might or might not happen, but if you deliberately throw this exception is because you know something could happen and if that is the case it needs to be properly catched and properly logged. And if the case is that no default behavior exists... what does suppose to mean? I mean, you need the int (variable being passed) that corresponds to one of them (enum), who is supplying you the status/ the int, whatever?, if you tell me you have different clients that don't know 100% of your enum, what does that mean to you?, you could say that you have a lot of clients, an angular, a 3rd party web api, etc, and you cant know for sure what they do, but then again, why would they supply you with a value that you expect to not be anything else that those values?.
    I don't know, is weird.

    • @gctypo2838
      @gctypo2838 Рік тому

      It means that any time this exception is thrown, it's an assertion that there is absolutely a bug that caused it. A "this should never happen" exception allows a presumed impossible situation to make a lot of noise if the programmer was wrong about their assumption. It's the ultimate "boneheaded" exception.
      It's not for use when a public method accepts an enum value and the caller is expected to not provide something out of range. For that, use ArgumentOutOfRangeException. It's for when (for example) a private method pulls an enum value from a field that should have been normalized or validated previously, and should theoretically never be possible to be called if that value is out of range. This exception is for programming defensively against yourself.

  • @yufgyug3735
    @yufgyug3735 2 роки тому +3

    Kind of reminds me of never type in Typescript

  • @EdKolis
    @EdKolis 2 роки тому

    Wouldn't all exceptions be UnreachableExceptions, if you're using exceptions as intended?

  • @kaushalkhamar3789
    @kaushalkhamar3789 2 роки тому +1

    What the hell but more professionall!!!🤣🤣🤣🤣

  • @5cover
    @5cover 2 роки тому

    I don't understand the point of these exceptions when you can use Debug/Trace . Assert/Fail?

    • @phizc
      @phizc 2 роки тому +1

      You can continue from a Debug.Fail. You can't from an exception. You also can't use Debug/Trace Fail in a switch expression.
      To use a variant of his enum example:
      string GetStatusName( PremiumStatus value ) =>
      value switch
      {
      Normal => "Normal status"
      Gold => "Gold status"
      Platinum => "Platinum status"
      _ => Debug.Fail( "This shouldn't happen" ); // this line would not even compile!
      }

  • @justinian.erdmier
    @justinian.erdmier 2 роки тому

    This is great!

  • @codesafariDev
    @codesafariDev 2 роки тому +2

    Nice, but should have called it "SurprisedPikachuFaceException"

  • @themiwi
    @themiwi 2 роки тому

    Huh, nice. Was abusing InvalidOperationException for this so far.

  • @gregcyrus2739
    @gregcyrus2739 2 роки тому

    ENUM is quite useful for bitwise operations (but friendly names by ENUM is insufficient for multi-lang apps). Better create classes instead of enums.

    • @phizc
      @phizc 2 роки тому +1

      You could create an extension method for the enum e.g. ToFriendlyName( this MyEnum value ). I don't see how using a class would be better in most cases.

    • @gregcyrus2739
      @gregcyrus2739 2 роки тому

      @@phizc Multi-Language support (with multiple translations to read from DB. How will you manage this in an Ext-Method; also to avoid multiple READs from DB)?

    • @phizc
      @phizc 2 роки тому

      @@gregcyrus2739 I assumed you wanted to display enum values in a localized way to the user. I don't understand why the DB value is localized.

    • @gregcyrus2739
      @gregcyrus2739 2 роки тому

      @@phizc how would you implement multi-lang friendly names in that enum-ext-method?

  • @adaslesniak
    @adaslesniak 2 роки тому

    Shouldn't as a rule all exceptions never be thrown? Except maybe ThreadAbortException which is not really an exception but conscious behavior. And what's unprofessional with "what the hell" - it's extremely informative to other programmers that this is something not expected by provider of this code, so they need to dig up deeply on their own. Anything "more professional" would by a lie that you know what happened.

  • @yooyo3d
    @yooyo3d 2 роки тому

    Why dot net throw exceptions when one use nonblocking sockets. By standard, in nonblocking, socket API return error -13 (err would block). Instead of error MS decide to throw exception. Because of that programmers has to use exceptions in programming logic, which is bad.

  • @AndrewSadin
    @AndrewSadin 2 роки тому

    Hello, Nick! How can i buy your courses from Russia? :o)))

  • @mensiuscho
    @mensiuscho 2 роки тому

    Then why is there, when never should be used?

    • @phizc
      @phizc 2 роки тому

      It shouldn't be *thrown* - not shouldn't be used. As the name suggests, it can be used to tell the compiler that the code is unreachable.
      E.g. you have a ThrowHelper class that has methods that throws an exception with a good description.
      [DoesNotReturn]
      static void ThrowArumentOutOfRangeException( int value, int maxValue, int minValue = 0, [CallerArgumentExpression( nameof(value) )] string name )
      => throw new ArgumentOutOfRangeExcpetion( $"The argument {name}' is out of range. The value was {value}. Minimum is {minValue}, Maximum is {maxValue}" );
      usage:
      int Square( int value )
      {
      const int min = 0, max = 5
      if( value is >= min and

  • @greenlemon9155
    @greenlemon9155 2 роки тому

    i dont really know anything about csharp but how the hell are you allowed to compile with a fuction not touching the returned register ?? this is undefined behavior

  • @benoitrousseau4137
    @benoitrousseau4137 2 роки тому

    Aww, it's a shame there's an official exception for this now, I've seen quite a few creative names for those logically impossible exceptions. 🤣

  • @gibbytravis
    @gibbytravis 2 роки тому

    Yes it needs to be more professional. "What the hell." should end with a question mark not a period.

  • @mo.azhdari
    @mo.azhdari 2 роки тому

    This kind of situation must be handled in compile time, not runtime. For example, in the enum case, we need exhaustive matching, not a new exception type. By the way is a better solution for handling enums default switch cases.

    • @nickchapsas
      @nickchapsas  2 роки тому +1

      Does it? By design my choice would be to throw an exception. It’s something that doesn’t or shouldn’t have a default case. If for some reason it happens, I want the thing to not move forward and notify me. Catching cases you don’t intent to handle leads to very weird processing where you make decisions around the problem instead of facing it

    • @mo.azhdari
      @mo.azhdari 2 роки тому

      ​@@nickchapsas Actually I agree with you, I've thought twice, and now I think what I said is irrelevant.

  • @GameDevNerd
    @GameDevNerd 2 роки тому

    public class ShouldNeverBeThrownException:
    System.InvalidOperationException {
    public const bool ShouldThrow = false;
    public const bool DontEverThrow = true;
    };

  • @ryan-heath
    @ryan-heath 2 роки тому

    "WTH" is more professional

  • @Mig440
    @Mig440 2 роки тому

    Switch expressions in java are much better here, the exhaustiveness checker will not allow your program to compile if you switch on an enum without covering all cases. Then again java enums are not like c# enums (glorified ints), so I can see why someone might use this. But really, why would you ever allow something like the expression (enum) 5 to compile in the first place 🤣

  • @BenjaminBrienen
    @BenjaminBrienen 2 роки тому +3

    This is disappointing. This exception is just a patch solution to a fundamental limitation of the expressiveness of the language. This construct should not have been created, and instead, they should have solved the root problem.

    • @francescodente2876
      @francescodente2876 2 роки тому +1

      This is partially true, at least with the "impossible enum value" case. However, in some cases this is a really nice-to-have feature, especially when the compiler is not able to detect unreachable code due to undecidability issues

    • @agsystems8220
      @agsystems8220 2 роки тому +1

      The root 'problem' is that we would like to be able to write efficient code based on theorems (or even hypothesis without proof) that the programmer knows, without having to justify them to the compiler. I don't want to have my hot code checking every single enum value for whether it is one of a finite set when I ask it to cast from a byte value, and I also don't want to have to prove to it that all the byte manipulations I do are closed within the enum. I want to be able to say to the compiler "Trust me, but tell me if you see something that shows I'm wrong".
      For example, let's say our enum is a description of an integer, with 1 being divisible by 3, 2 being divisible by 5, 3 being divisible by 15, and defaulting to 0. If we multiply 2 integer enum pairs we can either examine the result to work out it's enum, or perform a bitwise 'And' on the enums. The second is obviously far more efficient.
      Enums in particular can be constructed to represent something with structure, and those structures can be arbitrarily complex. Solving the root problem requires building an oracle, as finding whether a section of code can be reached is equivalent to the halting problem (easily proved, just set the program to restart instead of its normal halt, but halt at a location you choose).
      Possibly you mean some stronger Enum type that does not permit computation, and there are certain safety critical use cases for that. I don't think this is really warranted, because safety critical code should probably be using a whole host of other tools and language constraints anyway.

    • @mkwpaul
      @mkwpaul 2 роки тому

      ​@@agsystems8220 > I don't think this is really warranted, because safety critical code should probably be using a whole host of other tools and language constraints anyway.
      Tools and language constraints that C# doesn't provide.
      There is currently no way to write a truly safe or even a practically safe Enum Type in C# without major compromises, which IMO is a huge design flaw, especially for a language that's supposed to be high level.
      Well, one could effectively extend compiler checks via custom analyzers and then enforce those diagnostics to result in an error if they are triggered, but those would still not cover shenanigans via reflection. Maybe that would be possible, but that would be even more complicated than writing custom analyzers already is.

  • @phizc
    @phizc 2 роки тому

    I had a perfect use case for it earlier today. I made my own Guard class (based on CommunityToolkit.Diagnostics) to handle Dictionaries. I wanted it to be extensible, so I made a method that returns a struct (DictionaryGuard) and you could chain together checks. This way it's possible to add extra checks by using extension methods. CT.D has so many overloads that you get lots of ambiguity errors. E.g. There's an overload for ICollection, and IReadOnlyCollection. Many collections, like Dictionary, implement both.
    MakeSure.TheDictionary( myDict ).IsNotNull().HasCountGreaterThan( 5 );
    I copied some code from their ThrowHelper class to do the throwing. They always throws and are marked with DoesNotReturnAttribute. The problem is that the compiler can't rely on the DNR attribute to allow not returning the struct for chaining.
    public string Name { get; }
    public IDictionary Dictionary { get; }
    public DictionaryGuard HasCountGreaterThan( int count )
    {
    if( Dictionary.Count > count ) return this;
    ThrowHelper.ThrowArgumentExceptionForFailedHasCountGreaterThan( Dictionary, count, Name );
    // this is "reachable" according to the compiler, even though the previous method *always* throws.
    throw new UnreachableException(); // NotImplementedException( "This should be impossible" );
    }
    I used NotImplementedException then. I've now switched to UnreachableException.
    BTW. Even if the MakeSure and DictioryGuard does work, and I think it's cleaner than, say, a Guard.DictionaryHasCountGreaterThan method since it would be easy to add other methods like TheDictionary, it is about 65% slower. I benchmarked it to be 3.86 ns compared to 2.33 ns for CT.D's implementation. Still, you could call it 333 million times in a second so, meh.
    Edit:
    I wish C# would get support for shapes soon. That would solve the problem completely.
    e.g.
    shape SHasCount
    {
    int Count { get; }
    }
    public void HasCountGreaterThan( SHasCount collection, int count, [CallerArgumentExpression( nameof(collecton) )] string name = "" )
    {
    if( collection.Count > count ) return;
    // throw ArgumentExcpetion
    }
    Any class that has an Int32 Count property with a getter would be matched by the shape. No overloads for Dictionary, List, ICollection, IReadOnlyCollection etc, needed.

  • @petrhavlat5159
    @petrhavlat5159 Рік тому

    i love ur videos nick but right now i would prefer laura cox, lynyrd skynyrd or ozzy osborne, i m subscribing ur chanell cuz i am c sharp ddeveloper. an i do like to meet u in person. my girlfriend does not undersndme she s says thar our wor work is is strange. i wanna talk with somebody who understand code.. i m drunk today lets exchange techs im expert on robotics in csharp

  • @IvanRandomDude
    @IvanRandomDude 2 роки тому

    if (dotnet.IsNotAwesome)
    throw new UnreachableException("WTF");