{"id":503,"date":"2007-07-02T18:33:00","date_gmt":"2007-07-02T18:33:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/andrewarnottms\/2007\/07\/02\/how-to-not-write-an-especially-precarious-app-on-net-compact-framework\/"},"modified":"2019-04-03T22:50:51","modified_gmt":"2019-04-04T05:50:51","slug":"how-to-not-write-an-especially-precarious-app-on-net-compact-framework","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/premier-developer\/how-to-not-write-an-especially-precarious-app-on-net-compact-framework\/","title":{"rendered":"How to (not) write an especially precarious app on .NET (Compact Framework)"},"content":{"rendered":"<p><P>As the .NET Compact Framework developers work to add features, fix bugs, and refactor code, we often have to determine whether a given change could break existing customer code.&nbsp; The ideal is that NetCF 3.5 will run all apps that ran on NetCF 2.0 and 1.0.&nbsp; We run hundreds of apps and many, many tests before shipping each product to check backward compatibility.&nbsp; The .NET Framework (both desktop and CF) makes heavy use of internal classes to allow us the freedom to change the internals of the framework without breaking customer code.&nbsp; But there are still ways that customers can write apps that may break on future versions.<\/P>\n<H3>Compare on exception text<\/H3>\n<P>Some exception types are very general and don&#8217;t tell you much about the error.&nbsp; One of these is InvalidOperationException, which can be thrown for a wide variety of reasons for many classes in our BCLs.&nbsp; Developers usually look at the Exception.Message property to get an idea of what went wrong.&nbsp; This is by design.&nbsp; What is <EM>not<\/EM> by design is for developers to write code in their apps that looks at the Message property and makes code path decisions based on it.&nbsp; Here is an example: (highly contrived, admittedly)<\/P><!-- code formatted by http:\/\/manoli.net\/csharpformat\/ --><PRE class=\"csharpcode\">    <SPAN class=\"kwrd\">class<\/SPAN> Program {\n        <SPAN class=\"kwrd\">static<\/SPAN> <SPAN class=\"kwrd\">void<\/SPAN> Main(<SPAN class=\"kwrd\">string<\/SPAN>[] args) {\n            <SPAN class=\"kwrd\">try<\/SPAN> {\n                XmlSerializer serializer = <SPAN class=\"kwrd\">new<\/SPAN> XmlSerializer(<SPAN class=\"kwrd\">typeof<\/SPAN>(MailAddress));\n            } <SPAN class=\"kwrd\">catch<\/SPAN> (InvalidOperationException ex) {\n                <SPAN class=\"kwrd\">if<\/SPAN> (ex.Message == <SPAN class=\"str\">&#8220;System.Net.Mail.MailAddress cannot be serialized because it does not have a parameterless constructor.&#8221;<\/SPAN>) {\n                    <SPAN class=\"rem\">\/\/ Print message to user saying he chose a bad type to serialize<\/SPAN>\n                }\n            }\n            <SPAN class=\"rem\">\/\/ Success!<\/SPAN>\n        }\n    }<\/p>\n<p><\/PRE>\n<P>This can break in at least two instances: <\/P>\n<OL>\n<LI>The text in Exception.Message is localized to the computer running your app, so this code will break if it was run on, say a Spanish computer for example. \n<LI>A subsequent version of .NET Framework may change the Exception.Message text to be more descriptive, accurate or whatever.&nbsp; <\/LI><\/OL>\n<P>Either one of these likely cases may cause your code path to take a wrong turn (in this case to assume success).&nbsp; Instead, you should write code that can analyze exceptions based primarily on exception type and\/or error code (enumerable or integer) where available.<\/P>\n<P>Note: It is generally ok to display the Exception.Message to a user of an app in the form of a MessageBox or a log file (there are security considerations in doing this, however) and let the user choose how to proceed.<\/P>\n<H3>Compare on Exception type<\/H3>\n<P>Another bad way of doing exception handling is to do absolute equality checking on exception types.&nbsp; Here&#8217;s another bad (and contrived) example:<\/P><!-- code formatted by http:\/\/manoli.net\/csharpformat\/ --><PRE class=\"csharpcode\">    <SPAN class=\"kwrd\">class<\/SPAN> Program {\n        <SPAN class=\"kwrd\">static<\/SPAN> <SPAN class=\"kwrd\">void<\/SPAN> ThrowBadArgument(<SPAN class=\"kwrd\">int<\/SPAN> positiveValue) {\n            <SPAN class=\"kwrd\">if<\/SPAN> (positiveValue &lt;= 0)\n                <SPAN class=\"kwrd\">throw<\/SPAN> <SPAN class=\"kwrd\">new<\/SPAN> ArgumentException(<SPAN class=\"str\">&#8220;Must be positive&#8221;<\/SPAN>, <SPAN class=\"str\">&#8220;positiveValue&#8221;<\/SPAN>);\n        }\n        <SPAN class=\"kwrd\">static<\/SPAN> <SPAN class=\"kwrd\">void<\/SPAN> Main(<SPAN class=\"kwrd\">string<\/SPAN>[] args) {\n            <SPAN class=\"kwrd\">try<\/SPAN> {\n                ThrowBadArgument(-5);\n            } <SPAN class=\"kwrd\">catch<\/SPAN> (Exception ex) {\n                <SPAN class=\"kwrd\">if<\/SPAN> (ex.GetType() == <SPAN class=\"kwrd\">typeof<\/SPAN>(ArgumentException)) {\n                    <SPAN class=\"rem\">\/\/ Oops, the user provided a non-positive number!<\/SPAN>\n                }\n            }\n        }\n    }\n<\/PRE>\n<P>Now suppose that the developer (or vendor) supplying you with your ThrowBadArgument method decided it was more appropriate to throw an ArgumentOutOfRange exception.&nbsp; This would break your code and again cause your program to inappropriately assume success.<\/P>\n<P>Here is a corrected example:<\/P><!-- code formatted by http:\/\/manoli.net\/csharpformat\/ --><PRE class=\"csharpcode\">    <SPAN class=\"kwrd\">class<\/SPAN> Program {\n        <SPAN class=\"kwrd\">static<\/SPAN> <SPAN class=\"kwrd\">void<\/SPAN> ThrowBadArgument(<SPAN class=\"kwrd\">int<\/SPAN> positiveValue) {\n            <SPAN class=\"kwrd\">if<\/SPAN> (positiveValue &lt;= 0)\n                <SPAN class=\"kwrd\">throw<\/SPAN> <SPAN class=\"kwrd\">new<\/SPAN> ArgumentOutOfRangeException(<SPAN class=\"str\">&#8220;Must be positive&#8221;<\/SPAN>, <SPAN class=\"str\">&#8220;positiveValue&#8221;<\/SPAN>);\n        }\n        <SPAN class=\"kwrd\">static<\/SPAN> <SPAN class=\"kwrd\">void<\/SPAN> Main(<SPAN class=\"kwrd\">string<\/SPAN>[] args) {\n            <SPAN class=\"kwrd\">try<\/SPAN> {\n                ThrowBadArgument(-5);\n            } <SPAN class=\"kwrd\">catch<\/SPAN> (ArgumentException ex) {\n                <SPAN class=\"rem\">\/\/ Oops, the user provided a non-positive number!<\/SPAN>\n            }\n        }\n    }\n<\/PRE>\n<P>Note in this latter example how the throwing method is using the derived class.&nbsp; But catching the base class in this way allows us to catch either ArgumentException or ArgumentOutOfRangeException equally well.&nbsp; <\/P>\n<P>And if you need to check the exception type using an if statement (to check the type of Exception.InnerException for example) be sure to use the <STRONG>is<\/STRONG> keyword rather than Exception.GetType() == typeof(&#8230;).&nbsp; For example:<\/P><!-- code formatted by http:\/\/manoli.net\/csharpformat\/ --><PRE class=\"csharpcode\"><SPAN class=\"rem\">\/\/ good example<\/SPAN>\n<SPAN class=\"kwrd\">if<\/SPAN> (ex.InnerException <SPAN class=\"kwrd\">is<\/SPAN> ArgumentException) {\n    <SPAN class=\"rem\">\/\/ do stuff based on inner exception<\/SPAN>\n}\n<SPAN class=\"rem\">\/\/ Another good example (albeit harder to read)<\/SPAN>\n<SPAN class=\"kwrd\">if<\/SPAN> (<SPAN class=\"kwrd\">typeof<\/SPAN>(ArgumentException).IsInstanceOfType(ex.InnerException)) {\n}<\/p>\n<p><SPAN class=\"rem\">\/\/ BAD example<\/SPAN>\n<SPAN class=\"kwrd\">if<\/SPAN> (ex.InnerException.GetType() == <SPAN class=\"kwrd\">typeof<\/SPAN>(ArgumentException)) {\n    <SPAN class=\"rem\">\/\/ do stuff based on inner exception<\/SPAN>\n}\n<\/PRE>\n<H3>Finding public methods using reflection and parameter names<\/H3>\n<P>There&#8217;s no good way to do this.&nbsp; You should only find methods based on the parameter <EM>types<\/EM>, not parameter <EM>names<\/EM>.&nbsp; The reflection API makes it easy to do it right, and harder to do it wrong.&nbsp; Here is an example on how to do it right, and wrong:<\/P><!-- code formatted by http:\/\/manoli.net\/csharpformat\/ --><PRE class=\"csharpcode\">    <SPAN class=\"kwrd\">class<\/SPAN> Program {\n        <SPAN class=\"kwrd\">static<\/SPAN> <SPAN class=\"kwrd\">void<\/SPAN> Main(<SPAN class=\"kwrd\">string<\/SPAN>[] args) {\n            <SPAN class=\"rem\">\/\/ Good example<\/SPAN>\n            MethodInfo m = <SPAN class=\"kwrd\">typeof<\/SPAN>(<SPAN class=\"kwrd\">int<\/SPAN>).GetMethod(<SPAN class=\"str\">&#8220;ToString&#8221;<\/SPAN>, <SPAN class=\"kwrd\">new<\/SPAN> Type[] { <SPAN class=\"kwrd\">typeof<\/SPAN>(CultureInfo) });\n            Console.WriteLine(m.Invoke(3, <SPAN class=\"kwrd\">new<\/SPAN> <SPAN class=\"kwrd\">object<\/SPAN>[] { CultureInfo.CurrentCulture }));<\/p>\n<p>            <SPAN class=\"rem\">\/\/ Bad example<\/SPAN>\n            <SPAN class=\"kwrd\">foreach<\/SPAN> (MethodInfo method <SPAN class=\"kwrd\">in<\/SPAN> <SPAN class=\"kwrd\">typeof<\/SPAN>(<SPAN class=\"kwrd\">int<\/SPAN>).GetMethods()) {\n                ParameterInfo[] parameters = method.GetParameters();\n                <SPAN class=\"kwrd\">if<\/SPAN> (parameters.Length == 1 &amp;&amp; parameters[0].Name == <SPAN class=\"str\">&#8220;provider&#8221;<\/SPAN>) {\n                    m = method;\n                    <SPAN class=\"kwrd\">break<\/SPAN>;\n                }\n            }\n            Console.WriteLine(m.Invoke(3, <SPAN class=\"kwrd\">new<\/SPAN> <SPAN class=\"kwrd\">object<\/SPAN>[] { CultureInfo.CurrentCulture }));\n        }\n    }\n<\/PRE>\n<P>You should not take a dependency on parameter names of methods you call.&nbsp; The .NET compilers take care of this for you, but if you go around them by using reflection and use parameter names rather than parameter types to find the method overload you want, you&#8217;re asking to be broken if those method parameter names ever change (and they can!)<\/P>\n<H3>Finding internal-only methods or types&nbsp;using reflection<\/H3>\n<P>Again, there is no right way to do this.&nbsp; Getting into the internals of a library by using reflection requires full trust and means you&#8217;re just asking for your app to break in the next version of the library when those internals get changed.<\/P>\n<H3>In conclusion&#8230;<\/H3>\n<P>If you follow these tips, your apps will be more likely to perform well on current and future versions of the .NET Framework, on your own as well as your customers&#8217; locales.<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>As the .NET Compact Framework developers work to add features, fix bugs, and refactor code, we often have to determine whether a given change could break existing customer code.&nbsp; The ideal is that NetCF 3.5 will run all apps that ran on NetCF 2.0 and 1.0.&nbsp; We run hundreds of apps and many, many tests [&hellip;]<\/p>\n","protected":false},"author":2685,"featured_media":37840,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[106,4617,3918],"class_list":["post-503","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-permierdev","tag-net","tag-andarno","tag-netcf"],"acf":[],"blog_post_summary":"<p>As the .NET Compact Framework developers work to add features, fix bugs, and refactor code, we often have to determine whether a given change could break existing customer code.&nbsp; The ideal is that NetCF 3.5 will run all apps that ran on NetCF 2.0 and 1.0.&nbsp; We run hundreds of apps and many, many tests [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/503","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/users\/2685"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/comments?post=503"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/503\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media\/37840"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media?parent=503"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/categories?post=503"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/tags?post=503"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}