Exception Anti-Patterns in C# with Matt Eland - .NET 146
Matt Eland is a Microsoft MVP in AI. He joins the show to discuss "Top 10 Dotnet Exception Anti-Patterns in C# ". He talks about Exceptions in C# and what it is. He tackles teaching and knowing how to catch and throw exceptions. Moreover, he dives into some of the issues encountered.
Show Notes
Matt Eland is a Microsoft MVP in AI. He joins the show to discuss "Top 10 Dotnet Exception Anti-Patterns in C# ". He talks about Exceptions in C# and what it is. He tackles teaching and knowing how to catch and throw exceptions. Moreover, he dives into some of the issues encountered.
On YouTube
Sponsors
Links
Socials
Picks
Transcript
SHAWN_CLABOUGH:
Hello and welcome to another episode of Adventures in.NET. I'm Sean Clebo, your host, and with me today, your other co-hosts. We've got Christian Wins. Hey,
CHRISTIAN_WENZ:
Hi
SHAWN_CLABOUGH:
Christian. Hey.
CHRISTIAN_WENZ:
Hey Sean
SHAWN_CLABOUGH:
Eh, hey. And Adam, firm as it, firm as heck.
ADAM_FURMANEK:
Hello everyone, it's Furmanic as always.
SHAWN_CLABOUGH:
Promatic, I should know that,
ADAM_FURMANEK:
I'm
SHAWN_CLABOUGH:
but
ADAM_FURMANEK:
gonna
SHAWN_CLABOUGH:
it's
ADAM_FURMANEK:
go.
SHAWN_CLABOUGH:
my English tongue.
CHRISTIAN_WENZ:
There are so many permutations.
SHAWN_CLABOUGH:
It always does not go with my brain. So.
ADAM_FURMANEK:
It's funny when I used to live in the US, people never could understood me when I said my first name. In Poland, we call we say it Adam, and they always expect me to say Adam. So then I decided to just go with Bob when they asked in Starbucks.
SHAWN_CLABOUGH:
Hehehe
CHRISTIAN_WENZ:
old Starbucks trick, right? So either your name is Bob or your name is Chuck Norris, otherwise it will be misspelled.
ADAM_FURMANEK:
No.
SHAWN_CLABOUGH:
I'm sure if you say Adam, people think A-T-O-M. And so instead
ADAM_FURMANEK:
That
SHAWN_CLABOUGH:
of
ADAM_FURMANEK:
could
SHAWN_CLABOUGH:
Adam,
ADAM_FURMANEK:
be the reason,
SHAWN_CLABOUGH:
yeah.
ADAM_FURMANEK:
yeah?
SHAWN_CLABOUGH:
All right, well, let's bring in our guests. Let's welcome Matt Eland to the show. Yeah,
MATT_ELAND:
That
SHAWN_CLABOUGH:
welcome
MATT_ELAND:
is the correct
SHAWN_CLABOUGH:
Matt.
MATT_ELAND:
pronunciation. I was I was like, Oh, is he going to get it? Is he not? But yeah, Matt, even as what I go by. So it's it's honor to be here. I think again, I think I've been here before a couple of years ago, maybe. But it's it's it's great to be on the podcast.
SHAWN_CLABOUGH:
Yeah, well actually thinking about this with the last names, I think it's not fair. I'm doing the introduction to you guys. You guys never had to say my last name.
ADAM_FURMANEK:
That's fair,
SHAWN_CLABOUGH:
Figure
ADAM_FURMANEK:
that's
SHAWN_CLABOUGH:
it
ADAM_FURMANEK:
fair.
SHAWN_CLABOUGH:
out. Yeah.
ADAM_FURMANEK:
So we get prepared for the next episode, how about that?
SHAWN_CLABOUGH:
Okay, awesome, awesome. All right, Matt, why don't you get us started by giving us a brief introduction to yourself, kind of what you do, how you got into development, how you got into.NET.
MATT_ELAND:
Yeah, sure. So I'm that stereotypical guy who kind of grew up with programming. I grew up just like, oh, I can make a computer do all these awesome things as we had the 80s and arcades and Zork and all that fun stuff. And so I was in early college, actually, when.NET came around. And so my university, University of Wisconsin, they were looking to adapt.NET. And they wanted a student programmer to help them with that process. So. My job was actually to learn.NET back in Beta 1 and then teach their administrative information systems team. Like, here's how you.NET. You know how to Java. Here's how we.NET. Here's how it's different than C++ and that kind of thing. And I fell in absolute love with C Sharp and a little bit with VB as well. And I just pretty quickly looked to form my career around that language, around really the.NET platform. I have been in the software as a service industry for 15 years or so before I kind of went out of that industry and into teaching. So last three years, my whole job has been, once again, teaching.NET to other people along with SQL, JavaScript, HTML, all that fun stuff. I really love learning new things. I really love sharing those things with other people. And I have a particular interest in AI and machine learning. It's so interesting to be surprised by computers and what they can do. So I love tech. Love sharing it with other people. And that's a lot of fun.
SHAWN_CLABOUGH:
Should I say go Badgers or?
MATT_ELAND:
Yeah, no, no, I fled the state of Wisconsin. It's a lovely state, but we have more seasons in Ohio. So yeah, I'm now in Columbus, Ohio.
SHAWN_CLABOUGH:
Okay, yeah, my wife's from Wisconsin. So but she's no also fled the states. All right, so I think our topic for today to get us started, we're going to talk about exceptions. And you had an article that was talked about anti-patterns in C Sharp. And there's a good list, at least of 10 different anti-patterns that you've got. I guess we should just start at the beginning. And.
MATT_ELAND:
Yeah. So as an instructor, I see students like learning exceptions the first time, and there are all these common mistakes they tend to make. And this was me like, hey, what resource can I give to my students so that they can internalize this after I've taught it and remember it and recall it? But the first one is, you know, just not catching exceptions. People will write their code just assuming it's all going to be on the happy path and nothing will ever break. And then, well, that's fine until that file doesn't exist on your machine or your database goes down or you run out of disk space or whatever it might be. And so one of those early things that we see people do is they don't actually catch any exceptions whatsoever. And so understanding
CHRISTIAN_WENZ:
But
MATT_ELAND:
that.
CHRISTIAN_WENZ:
why is that the case, would you say? Is it because developers being careless, lack of time, unjustified optimism?
MATT_ELAND:
Well, I think in my students, it is optimism. You know, they're thinking about the program and did I write it correctly? It should work, right? And they're learning. You know, up to this point, I haven't had to worry about exceptions. I haven't done anything with the file input output, for example. And as long as I coded my code right, it worked fine. You know, they might get an index out of range or null reference exception or something like that. But that was something that if they coded it right, they weren't going to get that. And once they get into file system, database, it's like, oh, there are these things now that are outside of my control. And that's just part of that learning journey for those for those novices. Because I work with people who have in many cases never programmed before, never touched a computer in like that technical way beyond just using a web browser or whatever. So I get to help them through those those early journeys.
CHRISTIAN_WENZ:
I think it's similar like doing security. I mean, I do a lot of web security, right? And when I read through some beginner books, I just see, okay, so security is just an afterthought. But of course, if you're learning something, I mean, first you have to grasp whatever. Let's take a silly example. How can I read out query string parameters in a web application, right? And then, you know, I print them out. But of course, that could be cross-site scripting, but. If I start with security 101, Crosshead scripting defenses, etc. then people will probably ditch the book because they just say, no, I kind of want to learn the basics. Leave me alone with that JavaScript thing, which I try to avoid anyway. So if you mentioned that you teach developers, if you teach non-developers specifically, so when do you start introducing exceptions? how far along the path.
MATT_ELAND:
I teach a very accelerated program at Tech Elevator. But week four of teaching them, they get like four hours a lecture a day. Week four, I introduce them to the idea of file input. And at the same time, I introduce them to try catch and we're managing exceptions. Because that's really realistically the first time they want to catch an exception. Because before that, they'd be catching a null reference exception or an index out of range exception. Those are not ones that you generally catch. Maybe a format exception is an OK one to handle if the user typed in a string instead of a number, for example, and you're trying to parse it. But following the curriculum that I follow, as soon as they hit input, that's when realistically, hey, you need to worry about this file not being here. You need to worry about this being locked or whatever it might be. And that's usually a paradigm shift for them. Like, oh, I have no idea that I could write write code and the program might still error.
CHRISTIAN_WENZ:
Alright, so we have to catch our exceptions. But what then?
MATT_ELAND:
Well, I have some more things about how we catch exceptions, but I think that's a couple bullet points later on. The second thing, and this is this one is actually pretty hard for me to teach my new developers, is that sometimes it's OK to throw an exception. You know, if you're making a method or you're making a class and you are getting invalid arguments, you know, it is maybe preferable to throw in an argument out of range exception or something similar to that rather than. erring down the road or letting your application get into an invalid state. It can really be advantageous sometimes to have that exception get thrown as close as possible to things going wrong because that can help other people say, oh, I can't call this method with a negative parameter, for example. If I call this, I need to have this other exception or this other thing set up so that it's legal. So... teaching people it's okay to throw exceptions and that these are some exceptions that already exist and are okay for you to throw. That's one of those things I have to really deal with as an instructor for new developers in particular.
ADAM_FURMANEK:
And while speaking on this, so you mentioned like throwing when some parameter is incorrect, but what about throwing for instance, like there is when there is no business outcome of the method we're running? Do you think it's still okay to throw the exception or maybe you would opt for optional maybe or whatever type we have there coming from functional programming? What's your
MATT_ELAND:
Well, I don't get into functional programming with my students. The mantra that was beaten to me in college was, I think it's still a good one, is we don't use exceptions for flow control unless something is truly exceptional. Every time you're throwing an exception, there's a lot of things that have to happen for getting that current call stack and it's actually a fairly slow operation to throw an exception. So you want to try to avoid it if you can. Getting into functional programming, we have patterns like optionals and the ability to return back a null value, for example. I think those are really good practices. Those are maybe a little bit more intermediate than beginner-facing. So for my students, I say, hey, just return null or throw an exception. Those are appropriate things for you to do if something is going wrong. But as
ADAM_FURMANEK:
Sounds
MATT_ELAND:
time goes
ADAM_FURMANEK:
like
MATT_ELAND:
on, go
ADAM_FURMANEK:
a
MATT_ELAND:
ahead.
ADAM_FURMANEK:
good rule of thumb.
MATT_ELAND:
Yep. My next rule of thumb is about really what we catch. So.NET makes it really easy for you to catch anything you want. So you can write a try and then a catch, and you can catch exception. Every exception in.NET ultimately inherits from the exception class. I do wish the exception class was abstract. It is not. So it is possible to instantiate and throw an exception. That's another anti-pattern later on. But I see students being just too general in what they're catching. It's never a good idea to catch exception because then you are catching anything that might go wrong in your application and not like something very specific that you were expecting. We want to catch an exception at the appropriate place where we can respond to it. How do you know if you can respond to it if you don't even know what it is? It's okay to catch an IO exception, it's okay to catch a directory not found exception. But actually catching exception or application exception or whatever at a high level, that's where you start to assume just too much responsibility for the program flow for your application. And you might be trying to handle things that your application can't or shouldn't.
SHAWN_CLABOUGH:
I think a tough thing for beginners in doing this and reasons why they just resort to catching exception is they don't know the different exception types that their code and there could be end up throwing. So there's not a way you can't just, you know, often hover over something or whatever. And it says this type of exception could be thrown by this method. And so they just well, if I don't know, then how am I supposed to know what to catch?
MATT_ELAND:
Yeah, and I think Microsoft Learn has some pretty good documentation about the inheritance tree for exceptions, for at least the basic ones. And you can always, in Visual Studio, you can right click, you can say, find all references or find all implementations or something. But that's not something necessarily that a beginner is going to be comfortable with, necessarily. So certainly knowing what exception to throw, that's an obstacle. Knowing which exceptions to catch, that's an obstacle as well.
SHAWN_CLABOUGH:
Yeah, I think that's something that definitely could be made easier for the developer to know those things rather than have to go out to the docs or go through all the different inheritance tree of something and find that out.
MATT_ELAND:
Yeah, one of the things I think that Java does well during my foray into Java is that it does have the checked exceptions, it does tell you a little bit more explicitly, this method may throw this exception. More and more we're getting the ability to document that with XML comments and the like on methods and our tooling and Visual Studio is supporting that and surfacing that a little bit more. But I do wish we had more errors and warnings that would be a little bit more aggressive with you and saying, hey, this might happen if you call this method.
ADAM_FURMANEK:
That's a very interesting comment, especially the general sentiment in Java language is that checked exceptions was a mistake to be introduced. And C++ wanted to do the same. I mean, around C++ 03 or C++ 11, it was, I think, they introduced the declaration on the method which exceptions are being thrown. But a couple of years later, I don't recall whether it was 14 or 17 standard edition. they made that deprecated and don't want to go this direction.
MATT_ELAND:
Really? I had not heard that. That's very cool.
CHRISTIAN_WENZ:
What is the reasoning for that? I mean...
ADAM_FURMANEK:
For Java, definitely the biggest pain is that when you go with generic interfaces and by generic, I don't mean generic, but something you know, general, if you do not specify exceptions, then obviously the code implementing given interface cannot throw these exceptions because that would break the invariance, right? So imagine now taking C sharp language and taking your beloved func or action. And the bank, you can never throw from that lambda anymore because it doesn't declare exceptions, right? Or it
MATT_ELAND:
Mm.
ADAM_FURMANEK:
could just declare throwing exception, which would be next to useless, right? So that's the biggest issue. And lambdas are really harmed by that in Java language. And I mean, they have a very fancy thing they call Lombok, which is similar to like our 40 or kind of code generation behind the scenes. and they use a notation called sneaky throws that takes your exception and wraps it in a type that is called runtime exception, which does not need to be checked. So they hack things a bit. For C++
MATT_ELAND:
It's interesting.
ADAM_FURMANEK:
on the other hand, I don't know why they decided to deprecate that. I think the problem was because it was super hard to figure out what exceptions could be thrown. And now they have a thing that they call concepts. I'm not sure about the name, but anyway, they came up with something even better, as they always claim.
MATT_ELAND:
That's the nature of programming, we're always getting something a little bit better. There's one thing in C Sharp with exceptions that I wish that we didn't invent. And that was we simplified the language so you can say try and then catch and not specify what exception you're catching. Well, that's the same thing as catch exception. And I'm like, okay, I know the compiler doesn't yell at you, but I, as your instructor, am going to yell at you. Don't catch exception, because as soon as you catch exception, you're catching everything.
ADAM_FURMANEK:
And that's actually not something that was a syntax sugar. That's a historical thingy. Like I believe it was.NET 1 and also C sharp C++ CLI that allowed you to throw anything, not something that inherits from exception. So you could literally throw an integer. So this try without this catch, without specifying the type was not like... syntax sugar for cache exception, but that was the cache that would catch the integer that wasn't wrapped. I think they started around.NET 2 or.NET 4. I think it was 2. They started wrapping these things into something else like runtime handled exception or whatever, runtime wrapped exception. But long story, people probably don't even use that, don't even know this name.NET framework anymore.
MATT_ELAND:
It's still around, I still see it around there on job requirements and the like, but yeah. I am learning all sorts of new and horrifying things today.
CHRISTIAN_WENZ:
Alright, so let's move on to the list.
MATT_ELAND:
All right, my next one I have is similar to catch exception being an anti-pattern. Throwing an exception is an anti-pattern. Specifically throwing exception. So when you're saying throw new exception, that's bad because I wish the exception class was abstract. It's not. So you can instantiate it. You can throw an exception. But as soon as you throw an exception, instead of let's say an argument exception, invalid operation exception, overdraft exception, whatever. As soon as you just throw exception, if anybody ever wants to handle that, they are now forced to catch exception. Which, as we've been talking about, is an anti-pattern. So...
CHRISTIAN_WENZ:
Aren't you happy with the message property? I mean, you can have some funny magic strings in that. I mean, wouldn't you? I mean, wouldn't you?
MATT_ELAND:
The message is nice, but it forces me to, you know, just throw everything on the floor. And that's not the level of living dangerously that I like as a programmer.
CHRISTIAN_WENZ:
Absolutely. So why is the exception class not abstract?
MATT_ELAND:
I don't know.
CHRISTIAN_WENZ:
Is that
MATT_ELAND:
Probably
CHRISTIAN_WENZ:
also a
MATT_ELAND:
somebody.
CHRISTIAN_WENZ:
historical thing or?
MATT_ELAND:
That would be my assumption. I was in college when all this came out. So.
SHAWN_CLABOUGH:
I think it's Mark's fault.
CHRISTIAN_WENZ:
Probably.
SHAWN_CLABOUGH:
Hehehehe
MATT_ELAND:
So yeah, just don't throw exception. It's not too hard to throw a new specific exception, argument exception, whatever, invalid operation is always a favorite of mine. And you can always create your own exception class if you need to. So that one's pretty bad. This next one is one that I see all the time from professional coders. So going beyond our students and more people in the industry, this is one I see a lot. Let's say you're catching an exception and you want to... maybe log it, and now you're realizing, oh, I can't really handle this exception here. I need to throw this exception again, so something farther up the call stack can potentially respond to it. The syntax that people typically try is, let's say I'm doing a catch I-O exception EX, they might say throw EX, and that looks right. It looks absolutely right, and it's absolutely not right. I mean, it works. But as soon as you say throw ex, instead of let's say just throw, you are now clobbering your call stack. You no longer have the exact details of exactly where the exception originally occurred, because now it looks like the exception occurred inside of your catch block. And this one takes a little bit of time to explain, and usually it's good to show the call stack with and without doing this. I do wish that the language maybe was a little bit different on how it enforced throw. But the moral of the story is you want to do throw and not throw, you know, my exception here almost all the time. Sometimes you might want to hide exactly where the exception occurred. That's the exception to this, no pun intended. But most of the time, you know, if you see throw EX in your code, you're better off just saying throw.
CHRISTIAN_WENZ:
Would it be
SHAWN_CLABOUGH:
I haven't
CHRISTIAN_WENZ:
possible
SHAWN_CLABOUGH:
tried
CHRISTIAN_WENZ:
to
SHAWN_CLABOUGH:
to do
CHRISTIAN_WENZ:
just
SHAWN_CLABOUGH:
this.
CHRISTIAN_WENZ:
add a warning to the IDE?
MATT_ELAND:
I think there actually might be some code analysis warnings that do warn about this nowadays. If there aren't, certainly you could add one with Rosalyn.
SHAWN_CLABOUGH:
Yeah,
CHRISTIAN_WENZ:
Yeah,
SHAWN_CLABOUGH:
that
CHRISTIAN_WENZ:
exactly.
SHAWN_CLABOUGH:
was my my question was going
CHRISTIAN_WENZ:
Because
SHAWN_CLABOUGH:
to be,
CHRISTIAN_WENZ:
it's
SHAWN_CLABOUGH:
you
CHRISTIAN_WENZ:
throw,
SHAWN_CLABOUGH:
know,
CHRISTIAN_WENZ:
get and
SHAWN_CLABOUGH:
don't.
CHRISTIAN_WENZ:
catch, right?
SHAWN_CLABOUGH:
Don't linters catch, you know this. I haven't tried it lately because I never do it, but
CHRISTIAN_WENZ:
Yeah.
SHAWN_CLABOUGH:
you know that this seems like it should at least inform you that hey, this is not a best practice.
MATT_ELAND:
I think
CHRISTIAN_WENZ:
Might
MATT_ELAND:
I
CHRISTIAN_WENZ:
that
MATT_ELAND:
did.
CHRISTIAN_WENZ:
be a scenario where a throw EX is not a bad practice in a catch block?
MATT_ELAND:
Sure, sure. If you don't want anybody to know exactly where the exception occurred, if that's desirable behavior, which it might be, if that's a security
CHRISTIAN_WENZ:
Yeah,
MATT_ELAND:
concern
CHRISTIAN_WENZ:
okay.
MATT_ELAND:
for you, then sure. But that's going to be really rare. Most
CHRISTIAN_WENZ:
Yeah,
MATT_ELAND:
of the time, this
CHRISTIAN_WENZ:
I agree.
MATT_ELAND:
is not something somebody intended to do. And maybe the IDE is giving you like little yellow squigglies. But warnings just make your compiler go faster, right? So you don't need to read those, right? sarcasm
ADAM_FURMANEK:
I agree here. I agree here that generally I would love to see better support for that from the.NET platform. I think Python or at least CPython does it better. I mean you can't lose an exception. If there is one already like being thrown and handled when you throw a new one, then the previous one is like linked to the one you're just throwing. And especially that throw, I believe it's a special instruction on the intermediate language level. So platform really knows that, hey, an exception is being thrown. Why just not, you know, make our lives a bit easier? And this is also irritating with try and try and finally, where I mean, with the using construct, which is compiled to try and finally. because you can lose exception over there when it's being thrown like from the constructor or in some other places. So generally you can find fancy places where you do lose exceptions and then things get harder.
MATT_ELAND:
Yeah, the number of hours I've lost because something somewhere was catching some exception and I had no idea it was being caught and then the program was just running as normal, it's definitely not zero. I've spent days trying to figure out why is this not working and then come to find out someone was catching exception and then the program was just resuming fine. So the thing that we thought was loading fine was just not loading and being configured in the right way.
ADAM_FURMANEK:
Yeah, things can break, but when your logging infrastructure breaks, then you're really screwed.
MATT_ELAND:
Yep, yep, always nice to know when things break.
SHAWN_CLABOUGH:
So is it okay to just not catch all these things directly in your code and just catch them globally and handle them there?
MATT_ELAND:
It could be.
SHAWN_CLABOUGH:
It's.
MATT_ELAND:
It honestly could be. When you're building an ASP.NET web application, for example, you might not necessarily need to catch everything that goes wrong everywhere at every level. You might plug into your web server has some error handling built in that might be an appropriate place to handle exceptions. Right?
SHAWN_CLABOUGH:
Yeah,
MATT_ELAND:
But
SHAWN_CLABOUGH:
or
MATT_ELAND:
the point
SHAWN_CLABOUGH:
in the
MATT_ELAND:
is,
SHAWN_CLABOUGH:
full framework, you've got the global.asax file where you can just catch all your application errors there.
MATT_ELAND:
Yeah, and it is configured to like, let's say, to return a 500 internal server error on an error. That's often what you wanna do if an exception occurs anyway. You wanna make sure it's logged and you wanna make sure it returns a 500 and it doesn't leak any security information. ASP.NET handles a lot of that for you out of the box. But sometimes you need to catch your exceptions and do something differently if it occurs. And that's when you'll need your try catches. Um.
CHRISTIAN_WENZ:
Should that catching be close to the source for the exception or in general at least? I mean depending on what you want to do after the catch, just logging and then freeing resources or actually recovering from... I'll
MATT_ELAND:
Yeah, my motto
CHRISTIAN_WENZ:
be
MATT_ELAND:
is
CHRISTIAN_WENZ:
sure
MATT_ELAND:
that
CHRISTIAN_WENZ:
to...
MATT_ELAND:
I try not to be too too opinionated on where the handling happens. It should be at the appropriate level to recover from it. If we can recover at all, that's that tends to be my motto. And I make my decisions based on that. So ASP.net, I might not have a whole lot of manual catches at all because the web server is handling a lot of that stuff for me, versus if I'm building a desktop application. Yeah, I probably have some try catches scattered liberally throughout my code, as well as maybe a global exception handler. Because sometimes you'll say, hey, an error occurred trying to save this. Do you want to retry? That's something you'll want to present the user with. Versus a web server is a lot more stateless.
SHAWN_CLABOUGH:
I might be a little different than most people when it comes to exceptions because I like pain and if there's ever an exception, you know, where that happens where I didn't want it to, I get an email. You know, I mean, I log it in, I email because then it's like, I know it right away because my inbox is open for that. And it's like, if I get too many of them, it's like, okay, I need to take care of that because I don't want all those emails.
MATT_ELAND:
Yep.
SHAWN_CLABOUGH:
Sometimes
MATT_ELAND:
Yep.
SHAWN_CLABOUGH:
when things get logged, you just kind of like, I'll check on it every once in a while, rather than, you know, really knowing when it actually happened, if it's something that's important to know right away.
MATT_ELAND:
Now Sean, are you using something like some manual email logging code yourself or using a tool like Rollbar or App Insights or something like that to generate those?
SHAWN_CLABOUGH:
It I said I do send out to App Insights. That's kind of my logging tool there, but I just I have my own exception project handling class that I throw in there so that I get the emails with stacks and any custom information that I want to know about, parameters and things like that that happened at this point in time. So I get a good enough details in the email that I can get a good idea of what's going on. And then if I need more about, you know, what's the path somebody took to get to this error, then I'll get out to App Insights.
MATT_ELAND:
Our next one on this list is actually what we've talked about already. Using exceptions for flow control. Good idea or bad idea? Generally speaking, a bad idea. Your exceptions should be something that are just truly exceptional. So something very unexpected that you couldn't have checked for, that you couldn't have avoided. Exceptions are expensive in terms of time, in terms of code needed to catch them, et cetera. So you want to avoid using exception if you can. Having maybe a return type that might give you back a status or something like that might be appropriate. Certainly getting into functional programming, the option pattern is really, really handy because you can do something if it didn't, if it succeeded in something, if it failed. But I see a lot of students, especially when they're building like their first major project, they say, well, I usually need to return an integer, but sometimes I want to return. I don't know, something else. And so they just start throwing the exceptions just to manage their application and then pretty soon it's, it's, it's unmaintainable. Usually this is like they're violating the single responsibility principle. Anyway, that's what kind of leads them down this dark path. Uh, but be really careful about how you use exceptions. If it's not true, it's truly something just very exceptional, but just happened that you're responding to.
ADAM_FURMANEK:
Maybe one of the projects for your student you can suggest is just like you know there are fancy projects on the internet where the actual logic is implemented with mocks and where UDIB tests are not using mocks at all maybe let's do the opposite test everything without exceptions and use exceptions in business logic just to control all of that that could be fun if we could rewrite something big like ASP.NET in this way.
MATT_ELAND:
Yeah, that would be fun. Yeah, students and mocking, yeah, that tends to get the stomach turning for them. But it's something they need to learn anyway, so that's good.
ADAM_FURMANEK:
It always amazes me when I run big enterprise Java projects with Spring or whatnot. I literally get hundreds of exceptions when the application just starts up, hundreds. And I can see the big red log starting up. If there is no such a log, then I know something's wrong. But if I do see tons of red exceptions, all is good in Spring.
MATT_ELAND:
This is fine. That's great.
ADAM_FURMANEK:
Yeah, I'm burning cats.
MATT_ELAND:
All right, my next item, this is number eight of 10, if we're keeping track here, my next item on my list is not using custom exceptions. And what I mean by that is an exception is just a class. It's just something that inherits from exception ultimately. And so I see a lot of people trying to shoehorn in their custom business logic into, let's say an argument exception or something like that. The problem with that is, you know, once you start saying, hey, format exception is great and built into.NET, I'm going to use it over here for something completely unrelated. Now your catch format exceptions throughout your code are going to confuse new people who join your team. You might be reacting to two different things that happen. The correct way of doing this is if you've got something distinct enough for you and your application, just, you know, inherit from exception. Create a new exception type, inherit from exception. You'll need to. provide a number of constructors if you're designing it nicely, but you can create your own, let's say an overdraft exception or something like that, to manage your own logic. The really nice thing about this is once you have your own exception logic which takes about 10 lines of code, if you're doing it right, then you can catch that exception, and you know that catch block is only going to catch that type of thing. You can have other things inherit from that custom domain exception and the like. it makes your code a lot more easy to maintain and run. But I have to really lead my students to it. Like, hey, you know, you could make your own exception here. That's something you have control over.
SHAWN_CLABOUGH:
Is there a, you know, I guess the thing that I have about custom exceptions is you don't always think about it, you know, you know, that you should have this. So is there something, some sort of mindset or things you might identify that is sticks out that says, yeah, probably use some, a custom exception here rather than just one that's built in.
MATT_ELAND:
Yeah, usually it's something you might catch in code review, whether you're reviewing your own code prior to submitting it for somebody else or you're reading your coworkers code. But it's something like, oh, you're using argument exception a lot in this code. You're using invalid operation exception, using format exception. I'm not so sure this is the way this exception was originally intended. And we're using it in enough places that maybe we should be having our own one just for clarity. And so. It's not like a one-off thing, but once you see it repeated throughout your application for very similar things, that's when you start to say, usually in code review, oh, oh yeah, let's make something new. And that tends to lead to better code anyway. But of course, anything in tech can be abused and you can have a very deep inheritance tree with your own custom exceptions and the like, and it can get complicated quickly. But often custom exceptions can give you enough of the ability to kind of manage these exceptions to respond to them intelligently. Very much a fan of them generally speaking, but anything can be abused
CHRISTIAN_WENZ:
If you're
MATT_ELAND:
in tech.
CHRISTIAN_WENZ:
using a specific exception type for almost everything, then it's like using exception for almost everything, right?
MATT_ELAND:
Well, it's not quite that bad because you're not you're not catching all these built in things into dotnet. But yeah,
CHRISTIAN_WENZ:
Yeah.
MATT_ELAND:
it does devalue the exception that you're using a lot.
SHAWN_CLABOUGH:
So you might wanna use a custom exception when you're trying to classify the standard exceptions into different slots or pools, specific types of argument exception and things like that, rather than just looking at the message in something. So
MATT_ELAND:
Yeah,
SHAWN_CLABOUGH:
yeah,
MATT_ELAND:
just
SHAWN_CLABOUGH:
again.
MATT_ELAND:
to give you some specific examples, I spent what, nine years in the project management software as a service industry. We had custom exceptions for scheduling. We had custom exceptions for resource allocations and assignments and things like that. Major categories of things that can go wrong in your applications. If you start having a lot of these custom exceptions, you might be doing something wrong.
ADAM_FURMANEK:
you said earlier that you should use exceptions only for truly exceptional situations. But when you create your own exception, is it still a truly exceptional situation or maybe something you really foresee and expect?
MATT_ELAND:
That's a good question. I'm not sure I have a great answer for you there, Adam. I think that in any domain, you start to see the way that things can go wrong, typically based on your decisions that you made with your architecture. And that might lead you towards those types of exceptions that you tend to work with. If your bank is doing a lot of overdrafts, overdraft exceptions or something like that, well, I start to say, hey, couldn't we just use an if statement to avoid this? But if your architecture is maybe more eventual consistency or something like that. and you can't avoid it, then yeah, that might be a case where you might legitimately need to have these exceptions.
ADAM_FURMANEK:
And one thing that came to my mind now when talking about that stuff, what if some situation is not exceptional, but I really can't go without throwing the exception? I mean, for instance, trying to release a mutex or a synchronization primitive that I do not own, and there is no API for me to check, hey, am I the owner? Can I release it safely? And there is an exception. Should I catch it? What else can I do? And what if like, for instance, I need to do that in the catch block? So I will be tromming another exception inside the catch block and trying to catch it with nested try catch, like how to live? What to do?
MATT_ELAND:
So I like your example here. I'm going to give you a simpler example. I think that you're describing the scenario that we have in.NET with parsing. So we're doing it.parse. That's something where the user priorize a string, it may be an integer, it may not be an integer. You might argue that the case where it's not an integer is maybe not that exceptional, especially if the user is typing something in. In that case, then yeah, it might be okay to just throw in that case. Or you can expand your API and follow the same pattern we see in int.parse, for example, and say, hey, I've got int.parse, but I've also got int.triparse. And that will return a boolean that indicates whether or not it succeeded. Now, the triparse and the tri-x types of methods, they do typically use out parameters, which is something else that makes me want to go take a shower afterwards. But... It can help you avoid exceptions if that's what you're trying to avoid.
ADAM_FURMANEK:
Cool.
MATT_ELAND:
All right, number nine on my list, not providing enough exception details. So if you know you have to throw an exception and you're ready and you're there and you're throwing your exception, you gotta include enough information for A, the exception to have value when it gets logged. So you can maybe reproduce what went wrong later on and try to resolve that issue. And B, enough information for something to potentially respond to that. So. having properties on your exceptions, having a good message on your exception, having a helpful stack trace. These are all things that you really should be doing if you're doing an exception, because again, exceptions are expensive to create. They take some time to populate. And so you need to be responsible with that. You need to have value in those exceptions as you're adding in the data to them. So. Don't just throw a new invalid operation exception with nothing in there. You know, say, hey, here's what went wrong, here's a meaningful error message, here is maybe some properties that might be helpful. If it's related to a parameter, well, okay, I can maybe do an argument exception of some form. Give your future self enough information to do something with this exception when it shows up in an error log.
ADAM_FURMANEK:
One thing that I really hate when dealing with like error in a situation is like AWS. When you call the API and you get 403 with whatever thing you bang, you can't do that. And like literally no insight into what was going on. I understand security principles and whatnot, but there should be like big red button to link, include all the details you have about what I tried to do. especially that I've tried to code this, I don't know, AWS batch, and I'm getting some weird exception from S3 bucket. Why? Where? Same here
MATT_ELAND:
Yep.
ADAM_FURMANEK:
with exceptions.
MATT_ELAND:
You need to be able to reproduce what the user was trying to do in these cases. Otherwise, you're going to have all these bugs that you just can't resolve. You can't replicate. And you can't bring to a close in a speedy manner. Month that's ever happened with my code.
ADAM_FURMANEK:
One, sure, it's always someone else's code, right? One thing that I find really useful though is especially with like big corporate applications, very often when we throw the exception, then we kinda know what is the reason, like at least that's the idea, and we also know how to fix that. So sometimes just include the URL to your internal wiki with the page explaining how to fix that right in this exception, right? So when users hit this issue, they get, hey, this is where you go to fix the issue. One day, obviously, chat GPT will fix the code for us automatically, but as of now, let's do it this way.
MATT_ELAND:
Yeah, that's great. All right, number 10 of 10 on my list. We've actually already covered it.
CHRISTIAN_WENZ:
Yay!
MATT_ELAND:
I call it not trusting the framework. So we talked about ASP.NET earlier, and when I'm writing an ASP.NET API, I don't have a ton of try catches everywhere. I am trusting the framework to do its job, which is to serve up an API in a reliable and secure and repeatable manner. And if something goes wrong, it's gonna handle that. thing that went wrong. So for example, my students, I teach them try-catch, try-catch, try-catch. When you're working with a file, try-catch. When you're working with a database, try-catch. And boy, you need to use using so that you're actually closing your connection to the database afterwards. That's a painful lesson to learn right there. And so when I teach them ASP.NET, the week after that, they're like, oh, well, I need try-catch in my DAOs, data access objects, so that I can handle all this stuff. It's like, OK. Well. You did that, but now you're hiding the details of these SQL exceptions that are going on, and your API is returning a 200, but no data and things aren't actually happening. If you let the framework do its job and part of its job is exception management and exception handling, exception logging, then you have some standardized ways of expecting that, hey, this went wrong and we should respond to this by doing that. And that's also less code for you to maintain ultimately as well. You still need to make
ADAM_FURMANEK:
it.
MATT_ELAND:
sure that your connections are closed. But hopefully you can do that in a systematized way using iDisposable and other things.
ADAM_FURMANEK:
Yeah, exactly. Unless the framework does things that you don't want it to do. For instance, Stack Overflow exception or unhounded exception on the thread, they like terminate your application immediately and you cannot necessarily cope with that, right? So not trusting the framework has actually a way different meaning in that case.
MATT_ELAND:
Yeah, and sometimes, you know, so maybe we should say not trusting the framework without good reason. Because in that case you had a good reason to be paranoid. Yeah, you had to manage that yourself.
ADAM_FURMANEK:
Yeah, I love this example about catching Stack Overflow exception in.NET. In.NET, in C Sharp, you just can't do that. In Java, you can do that. Java is OK with handling exceptions like that. But in.NET, I love this. When I open up the documentation of Stack Overflow exception, Microsoft tells you, hey, you shouldn't throw the exception. Just check if you have enough stack for your operation. Right? And then I go to divide by zero exception. And they don't tell you, hey, check if you're trying to divide by zero, right? The difference is Stack Overflow kills you. That's a division by zero. That's not.
MATT_ELAND:
It's getting harder and harder to divide by zero intentionally. Trust me, I try to do it for teaching lecture, but yeah, it's still possible.
ADAM_FURMANEK:
That is true. That is true.
CHRISTIAN_WENZ:
So in order to trust the framework, you have to know the framework well enough, right? So the trust needs to be earned. So in order to trust the framework, you have to know the framework well enough, right? So the trust needs to be earned.
MATT_ELAND:
Yeah, and anything that you use long enough will betray that trust. But then you start to say, OK, well, where are the places where this falls apart? Where are the places where this isn't going to work for me? And so you build your logic over time with that.
ADAM_FURMANEK:
I would also add here the case of actually using different framework originally and then running in some different place. That doesn't happen that much in.NET, like.NET Core,.NET Framework, and Xamarin and other stuff. They are kind of consistent with each other. But for instance, if you try running Java code on the.NET platform, which is doable easily with like IKVM, then... As I already mentioned, Java doesn't care about unhunted exceptions. That was an exception, sure, fearing your application can carry on. But.NET is kind of picky about that and will just terminate your process. So if you take blindly Java code, sometimes you can change it as the other store. But if you just take it blindly and run on.NET, you may be surprised that, well, your framework now is a little bit against you and what you're doing.
MATT_ELAND:
this conversation is so great. You always get to learn all these deep, dark secrets and things that are going on in other languages. It's so great to chat with you all today.
CHRISTIAN_WENZ:
And it's always worth looking over the fence, right? So to see what our languages are doing.
SHAWN_CLABOUGH:
So does any of this change for multi-threaded applications? Makes it a lot more difficult when you're talking about threads, catching threads and where the stack trace is, where that information is.
MATT_ELAND:
Yeah, well, dealing with multi-threading, your exceptions tend to be wrapped in aggregate exception and other things, right? So it gets harder and harder and harder to deal with where that exception occurs. I gotta be honest, it's been a while since I've touched truly multi-threaded code. Just, it's not something I do a lot as an instructor anymore. Christian, Adam, what are your experiences?
ADAM_FURMANEK:
For me, actually, the multi-threaded is, as you mentioned, you have aggregate exception and it kind of works. But also async code is very nasty here, especially when we are talking about tasks, because async changes like stack traces a lot. And also sometimes it's incompatible with TPO. So you may again lose some exceptions just because in TPO world you could create a tree of your tasks. While with async you just follow the linear flow. But still, if someone mixes that with the old TPL code, then exceptions may just go unnoticed.
CHRISTIAN_WENZ:
Async is always hard. The async keywords make things look easy. It's hard.
MATT_ELAND:
it's still easy to make mistakes with async. You know,
CHRISTIAN_WENZ:
Oh, yes.
MATT_ELAND:
the first one you make is, OK, async void. No, no, no, no, no, no, that needs to be async task. Yeah.
CHRISTIAN_WENZ:
Yeah, but maybe an ASIC void is something where the IDE complains, right? I think the problem here is, it's kind of like the Pareto principle, right? 80% of times when you just slap some ASIC and await all over your code, it magically kind of works, right? Because essentially it's syntax sugar, but those remaining 20%, they are tough.
ADAM_FURMANEK:
you're a muted sound.
SHAWN_CLABOUGH:
Alright Matt, so do you have any last thoughts or tidbits of information on exceptions before we move on to pics?
MATT_ELAND:
I don't think so. I think my biggest things that I stress with my students are that exceptions aren't there to hurt you. They're there to help you, but don't abuse them. If you're finding yourself catching exception, if you find yourself throwing exception, you're doing something wrong. If you find yourself not thinking about exceptions, you're probably doing something wrong too. You need to be thinking about how your code might break and the like. But the more I go on as a technologist, the more I start to go into this. how can I write my application so that it responds appropriately to errors? And that's things like leading on the compiler more to catch these things with warnings and stricter checks, languages like F sharp, things like options, they make it harder and harder and harder for these exceptions and things like that to exist or for them to exist without us knowing about them. That's where I hope the language is gonna continue to grow is with our exception management and. patterns around that. But I think it's really exciting to work in.NET. There's just some early hurdles that we have to go through with learning how to correctly manage exceptions and that these things aren't evil or there to hurt us or things that should be ignored.
SHAWN_CLABOUGH:
Okay, great thoughts.
CHRISTIAN_WENZ:
Excellent.
SHAWN_CLABOUGH:
And yeah, we'll definitely put links to all these articles and the points that you covered in our show notes. So, yeah, great. We'll move on to picks then. So let's do Christian, do you wanna go first with your pick? So, Christian, do you wanna go first with your pick? So,
CHRISTIAN_WENZ:
Yeah, I have a rather unusual pick this time. And please don't laugh at me while I'll talk about the... Yeah, exactly. That's the first reaction I get. And maybe it's even justified. So jQuery 3.7 came out. And why do I think that's noteworthy? Because it still runs on, depending on statistics, 75% of the web. And of course, often because it's part of the template, right? And people are not actively using it. But I think it's probably one of the quality wise, one of the best open source projects. If you just look at the number of open issues, which is, I don't know, 10 or something, right? And most of them are support requests and compared it to some other open source projects where you get a few thousands. The quality is really great. And of course, you can debate whether in this day of age, there's still need for jQuery because many, well, most of the jQuery features are in a way now easily accessible in a cross-browser fashion. But then on the other hand, they are compensating for so many minor browser glitches, which is just amazing. It's something you would never catch with your own code in your own application. does not run on three quarters of the web, right? So you don't even have that big of a test bench. So in case you are still using jQuery, be aware, there's a new version. And it does not come with a security fix. So there's no great rush to use it. But it does come, of course, with bug fixes and also performance improvements for certain types of selections, for instance. And therefore I thought I would give a nod to the most popular JavaScript library out there. And I think it's still part of most ASP.NET templates.
ADAM_FURMANEK:
That's what you call epic.
SHAWN_CLABOUGH:
All right.
CHRISTIAN_WENZ:
Indeed.
SHAWN_CLABOUGH:
Can you beat that, Adam?
ADAM_FURMANEK:
No, I don't think so, no way. I was going to mention Disk2VHD, completely different stuff. This is a tiny tool from CIS internals from Microsoft that can turn your life system into a VHD file. First, it sounds like magic and it does do like a lot of magic. But second thing, an actual use case where I found it really useful was I was running a VPS and I wanted to migrate it to a dedicated server. But I was super lazy to reinstall all the stuff. So what I could do is I turned the VPS into the VHD file, uploaded it to the dedicated server and then boot that directly from the VHD file, something that started with Windows 7. and this really cool feature of Windows. So yeah, I have to recommend that works like a charm, but still jQuery is way ahead.
CHRISTIAN_WENZ:
I'm a big fan of this 2BHD though. So also for those old laptops which you're about to throw away, but still you may need it, you may need it. And then 15 minutes later, you just have a VHD and can use it.
SHAWN_CLABOUGH:
Okay, Matt, what's your pick for us?
MATT_ELAND:
Well, I got two. I got one that I got recently to celebrate. I'm getting to do my first online course in the near future. So stay tuned for some announcements on that. And I got a Lego kit to celebrate that. That's the T-Rex escape kit from Lego. But it's it's a Jurassic Park, the scene where the the T-Rex has gotten out of its paddock. Sorry, spoilers from the 1990s. And it's. stepping on one of the Land Rovers and it's just great. I don't know where in my basement I'm going to put the dang thing, but it's beautiful. So that was a lot of fun to put together. About a hundred bucks, I believe.
CHRISTIAN_WENZ:
I love the fence that just breaks thanks to the dinosaurs.
MATT_ELAND:
It's great. Yeah.
SHAWN_CLABOUGH:
Is there
CHRISTIAN_WENZ:
Fantastic.
SHAWN_CLABOUGH:
a Lego Jeff Goldblum to go along with that? Hehehehe.
MATT_ELAND:
There is, yeah. They have a pair of flares as well. So he's just sitting in his car. It's great. And then, of course, they get a Grant and a Timon Lex and the little night vision goggles. It's fantastic.
SHAWN_CLABOUGH:
Very cool.
MATT_ELAND:
My tech pick would be Polyglot Notebooks. Polyglot Notebooks is a open source data science, well, maybe not even necessarily just data science, but it's an open source notebook environment. project run by Microsoft, but it lets you run C-sharp, F-sharp, JavaScript, HTML, PowerShell, and all these other languages, SQL, KQL, in a notebook inside of VS Code. You can run all these little cells, mix together your cells and markdown, and it's just fantastic. I do a lot of data science experiments with Python and Jupyter notebooks. This is built on top of Jupyter, but you can use your.NET languages and tooling. I'm just loving nerding out in.NET and data science, data analytics and the like. And it's super cool, super fun. I'm doing a lot of writing and speaking on that right now. I love it so much. That's Polyglot Notebooks.
SHAWN_CLABOUGH:
Okay, so my pick this week is an online game and it's one of those types of games where if you've got an office party where you're responsible for coming up for a game for the group to play at the end of the if party or the meeting or whatever. This kind of works as a quick thing to throw out there. And it's basically Pictionary online and it's called Scribble. So if you go to scribble.io and it's S-K-R-I-B-B-L dot I-O. lets a group of players join in there and put in some words for everybody to guess. And then you can draw with your mouse or if you've got a drawing pad, you can use one of those if you want to make your drawings look a little bit better. But it's quick, it's fun, it's one of those that, lots of different things similar to this. But yeah, if you've got, if you get that responsibility for a meeting, yeah, try Scribble.io. and it's free so that's that's cool. All right, Matt, so if our listeners have questions and they want to get in touch with you, what's the best way to do that?
MATT_ELAND:
Well, best ways of getting in touch with me are generally Twitter or LinkedIn, but you can go to Matt Eland dot dev. That's M-A-T-T-E-L-A-N-D dot dev. And that's going to have all my links to my content, my YouTube channel, my blogs, my socials, LinkedIn, et cetera. So that's a that's a great one page place to go to find my stuff. But I'm generally very responsive on either LinkedIn or Twitter. And you can also email me as well at Matt at Matt on data science dot com.
SHAWN_CLABOUGH:
Great. And if our listeners want to get in touch with the show, they can reach out to me. I am on Twitter. I am at dot net superhero. Thanks, Mark. It was great talking about exceptions with you.
MATT_ELAND:
A lot of fun to be here.
SHAWN_CLABOUGH:
Great. And we'll catch our listeners on the next episode of Adventures in.NET. Bye all.
ADAM_FURMANEK:
Bye.
Exception Anti-Patterns in C# with Matt Eland - .NET 146
0:00
Playback Speed: