{"id":39683,"date":"2004-04-22T07:03:00","date_gmt":"2004-04-22T07:03:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2004\/04\/22\/cleaner-more-elegant-and-wrong\/"},"modified":"2004-04-22T07:03:00","modified_gmt":"2004-04-22T07:03:00","slug":"cleaner-more-elegant-and-wrong","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040422-00\/?p=39683","title":{"rendered":"Cleaner, more elegant, and wrong"},"content":{"rendered":"<p>\nJust because you can&#8217;t see the error path doesn&#8217;t mean it doesn&#8217;t exist.\n<\/p>\n<p>\nHere&#8217;s a snippet from a book on C# programming, taken from the chapter\non how great exceptions are.\n<\/p>\n<blockquote CLASS=\"q\">\n<pre>try {\n  AccessDatabase accessDb = new AccessDatabase();\n  accessDb.GenerateDatabase();\n} catch (Exception e) {\n  \/\/ Inspect caught exception\n}\npublic void GenerateDatabase()\n{\n  CreatePhysicalDatabase();\n  CreateTables();\n  CreateIndexes();\n}\n<\/pre>\n<p>Notice how much cleaner and more elegant [this] solution is.\n<\/p><\/blockquote>\n<p>\nCleaner, more elegant, and wrong.\n<\/p>\n<p>\nSuppose an exception is thrown during CreateIndexes().\nThe GenerateDatabase() function doesn&#8217;t catch it, so the\nerror is thrown back out to the caller, where it is caught.\n<\/p>\n<p>\nBut when the exception left GenerateDatabase(), important\ninformation was lost: The state of the database creation.\nThe code that catches the exception doesn&#8217;t know which step\nin database creation failed.  Does it need to delete the indexes?\nDoes it need to delete the tables?  Does it need to delete\nthe physical database?  It doesn&#8217;t know.\n<\/p>\n<p>\nSo if there is a problem creating CreateIndexes(), you leak\na physical database file and a table forever.\n(Since these are presumably files on disk, they hang around\nindefinitely.)\n<\/p>\n<p>\nWriting correct code in the exception-throwing model is in a sense\n<i>harder<\/i> than in an error-code model, since <i>anything<\/i>\ncan fail, and you have to be ready for it.\nIn an error-code model, it&#8217;s obvious when you have to check for\nerrors: When you get an error code.\nIn an exception model, you just have to know that errors can occur\nanywhere.\n<\/p>\n<p>\nIn other words, in an error-code model, it is obvious when somebody\nfailed to handle an error: They didn&#8217;t check the error code.\nBut in an exception-throwing model, it is not obvious from looking at\nthe code whether somebody handled the error, since the error is not\nexplicit.\n<\/p>\n<p>\nConsider the following:\n<\/p>\n<pre>\nGuy AddNewGuy(string name)\n{\n Guy guy = new Guy(name);\n AddToLeague(guy);\n guy.Team = ChooseRandomTeam();\n return guy;\n}\n<\/pre>\n<p>\nThis function creates a new Guy, adds him to the league, and\nassigns him to a team randomly.\nHow can this be simpler?\n<\/p>\n<p>\nRemember: Every line is a possible error.\n<\/p>\n<dl>\n<dt>\nWhat if an exception is thrown by &#8220;new Guy(name)&#8221;?<\/p>\n<dd>\n<p>\nWell, fortunately, we haven&#8217;t yet started doing anything, so\nno harm done.\n<\/p>\n<dt>\nWhat if an exception is thrown by &#8220;AddToLeague(guy)&#8221;?<\/p>\n<dd>\n<p>\nThe &#8220;guy&#8221; we created will be abandoned, but the GC will clean that up.\n<\/p>\n<dt>\nWhat if an exception is thrown by &#8220;guy.Team = ChooseRandomTeam()&#8221;?<\/p>\n<dd>\n<p>\nUh-oh, now we&#8217;re in trouble.  We already added the guy to the league.\nIf somebody catches this exception, they&#8217;re going to find a guy in the\nleague who doesn&#8217;t belong to any team.\nIf there&#8217;s some code that walks through all the members of the league\nand uses the guy.Team member, they&#8217;re going to\ntake a NullReferenceException since guy.Team isn&#8217;t initialized yet.\n<\/p>\n<\/dl>\n<p>\nWhen you&#8217;re writing code, do you think about what the consequences\nof an exception would be if it were raised by each line of code?\n<strong>You have to do this if you intend to write correct code<\/strong>.\n<\/p>\n<p>\nOkay, so how to fix this?  Reorder the operations.\n<\/p>\n<pre>\nGuy AddNewGuy(string name)\n{\n Guy guy = new Guy(name);\n guy.Team = ChooseRandomTeam();\n AddToLeague(guy);\n return guy;\n}\n<\/pre>\n<p>\nThis seemingly insignificant change has a big effect on error\nrecovery.  By delaying the commitment of the data (adding the guy\nto the league), any exceptions taken during the construction of the\nguy do not have any lasting effect.  All that happens is that a\npartly-constructed guy gets abandoned and eventually gets cleaned up by GC.\n<\/p>\n<p>\nGeneral design principle:  Don&#8217;t commit data until they are ready.\n<\/p>\n<p>\nOf course, this example was rather simple since\nthe steps in setting up the guy had no side-effects.\nIf something went wrong during set-up, we could just abandon the\nguy and let the GC handle the cleanup.\n<\/p>\n<p>\nIn the real world, things are a lot messier.\nConsider the following:\n<\/p>\n<pre>\nGuy AddNewGuy(string name)\n{\n Guy guy = new Guy(name);\n guy.Team = ChooseRandomTeam();\n guy.Team.Add(guy);\n AddToLeague(guy);\n return guy;\n}\n<\/pre>\n<p>\nThis does the same thing as our corrected function, except that somebody\ndecided that it would be more efficient if each team kept a list of members,\nso you have to add yourself to the team you intend to join.\nWhat consequences does this have on the function&#8217;s correctness?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Just because you can&#8217;t see the error path doesn&#8217;t mean it doesn&#8217;t exist. Here&#8217;s a snippet from a book on C# programming, taken from the chapter on how great exceptions are. try { AccessDatabase accessDb = new AccessDatabase(); accessDb.GenerateDatabase(); } catch (Exception e) { \/\/ Inspect caught exception } public void GenerateDatabase() { CreatePhysicalDatabase(); CreateTables(); [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-39683","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Just because you can&#8217;t see the error path doesn&#8217;t mean it doesn&#8217;t exist. Here&#8217;s a snippet from a book on C# programming, taken from the chapter on how great exceptions are. try { AccessDatabase accessDb = new AccessDatabase(); accessDb.GenerateDatabase(); } catch (Exception e) { \/\/ Inspect caught exception } public void GenerateDatabase() { CreatePhysicalDatabase(); CreateTables(); [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/39683","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=39683"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/39683\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=39683"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=39683"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=39683"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}