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
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???"
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 “¯\_(ツ)_/¯”
Well, "What a Terrible Failure" is a valid log level in android, I see no problem using it in c# code
I use "say WHAAAAAAT?"
"How did we even get here champ?"
Unreachable exceptions are the best place to hide easter-eggs while maintaining a facade of professionalism
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.
Once I pushed something like this, but it was rejected in review... perhaps because I called it "SchroendingerException" :P
I don't get why it was rejected... The name can't be cooler! 😂😂
Ahh, @@jorsang1, so nice to see you here :D
@@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-
@@phizc No coder I've met likes diacritics in class names ;)
Second!
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.
Good thing Nick isn't working on .NET 7. Otherwise it would be the "WhatTheHellHappenedException" class!
underrated comment
Cool name for that 🤣
** "but more professional" lol
Should I stay or should I throw now?
If I throw, there will be trouble
And if I stay it will be `double`
The elusive TeamRocketException.
@@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!
"AggregateException of all tasks was null. What the hell" actually sounds pretty professional to me 🙃
I've received unreachable exceptions because there was some funky stuff with marshalling and pinvoke
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.
I personally disagree. NotImplemented implies that there might be an implementation coming. Unreachable indicates state that should never happen under normal circumstances
@@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.
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).
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.
NotSupported would be better than NotImplemented but I still thing it doesn’t convey the truth about the situation.
20 years ago we called it assertions ;-)
I would love to hear about your exception alerting system and app monitoring in general.
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.
the hell what is happening here but better 😂love this one
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 :)
Same with Rust... and hopefully other modern languages.
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
@@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 :)
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?
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
I've never used Trace, but you can think of Asserts as nags that don't interrupt execution, and exceptions as errors that do.
6:32 "What the hell" 🤣
Which IDE are you using?
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.
imo, casting an int to enum should only be possible in unsafe code blocks, just like the pointer operations
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.
@@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
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.
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.
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.
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.
Kind of reminds me of never type in Typescript
Wouldn't all exceptions be UnreachableExceptions, if you're using exceptions as intended?
What the hell but more professionall!!!🤣🤣🤣🤣
I don't understand the point of these exceptions when you can use Debug/Trace . Assert/Fail?
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!
}
This is great!
Nice, but should have called it "SurprisedPikachuFaceException"
Huh, nice. Was abusing InvalidOperationException for this so far.
ENUM is quite useful for bitwise operations (but friendly names by ENUM is insufficient for multi-lang apps). Better create classes instead of enums.
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.
@@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)?
@@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.
@@phizc how would you implement multi-lang friendly names in that enum-ext-method?
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.
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.
Hello, Nick! How can i buy your courses from Russia? :o)))
Then why is there, when never should be used?
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
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
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. 🤣
Yes it needs to be more professional. "What the hell." should end with a question mark not a period.
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.
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
@@nickchapsas Actually I agree with you, I've thought twice, and now I think what I said is irrelevant.
public class ShouldNeverBeThrownException:
System.InvalidOperationException {
public const bool ShouldThrow = false;
public const bool DontEverThrow = true;
};
"WTH" is more professional
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 🤣
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.
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
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.
@@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.
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.
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
if (dotnet.IsNotAwesome)
throw new UnreachableException("WTF");