
A few days ago someone asked, and I dutifully obliged.
The “requirement” was to develop “one simple .net page”; quickly of course. One page that that will show the list of property values of the Request.Browser
(an .NET object), to the browser user. This “requirement” (again very quickly) turned out to be a very different requirement, after all. Mainly because it turned out that “show in the browser”, when translated into reality, actually meant: “send back to JavaScript behind a browser page that called the .net component…”. Ok, not to worry, requirements are ever moving targets, so here we go …
Of course, by now you know: this “simple page” is actually one server side component (implemented as an aspx component with no UI). This aspx obviously will grow in complexity very quickly, and it will change very often, as it seems obvious. Therefore, it was obvious too, I needed some design here. Instead of the sometimes usual development methodology called: “do it quick and dirty, till 5:30”. Which boils down to ad-hoc code, which will make changes more and more expensive and time consuming, as time passes by. While requirements keep on changing and new ones keep on coming.
After this high drama entry, back to job at hand. This component will be obviously “asked” to return objects encoded as JSON strings, since it will be called almost exclusively from javascript behind a browser hosted page. Furthermore: I am sure that due to future changes, it will need to send back, many different kind of objects from many diferent server side sources or .net objects. First example of this are property values of the server side Browser object. Made by ASP.NET runtime, for each browser request by looking into HTTP Server variables made after the connection with the browser.
Next example might be properties of some “home made” Configuration object. And so on. Obviously client side javascript ajax developers will want to use this component, to “see” what is “lurking” on the server side.
Then, rather quickly, I realized this is a time for (yet another) Factory pattern implementation. And then, I confidently went along to do this aspx, with a “help” of C# generics. Just to quickly realize the height of stone walls, that C# raises in front of someone coming from C++, perusing his C++ templates experience.
In short: C# generics are very restrictive (when compared to C++) and one can not simply transfrom C++ template concepts, into C#. It is actually impossible and it requires paradigm shift. Different thinking altogether. Different designs and implementation idioms. Without further delay, and after some wrestling with C# features and idioms, this is what I came up with.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
/* GPL (c) 2009-2010, 2019 by DBJ.ORG */ using System; public partial class Composer : System.Web.UI.Page { /// /// Transform particular property name/value pair into the json object /// ///host of the property ///info type of the property /// json object string prop_value_json (object host, System.Reflection.PropertyInfo pi_) { string retval = ""; try { if (pi_.CanRead) { if (pi_.PropertyType.IsNotPublic) retval = "NOT PUBLIC"; else if (pi_.IsSpecialName) retval = "SPECIAL NAME"; else retval = pi_.GetValue(host, null).ToString(); } else { retval = "CAN NOT READ"; } } catch (System.Exception x) { retval = x.Message; } return string.Format("'{0} \"{1}\" : \"{2}\" {3}'", "{", pi_.Name , retval, "}" ) ; } //---------------------------------------------------- protected void Page_Load(object sender, EventArgs e) { // use the browser property from the request object System.Web.HttpBrowserCapabilities browser = this.Request.Browser; // start the javascript code generation, that will send back string js = "var properties = [ '" + browser.GetType().Name + "', " ; // use our iterate_over method to encapsulate browsers properties collection // and exposure for iteration // NOTE: type of the iterator is correlated to the second argument foreach (System.Reflection.PropertyInfo member in iterate_over( browser, typeof(System.Reflection.PropertyInfo)) ) { js += (prop_value_json(browser, member) + " ,"); } // finish the javascript code, close the array declaration js += "]" ; // remove the last comma and add code to write the array of properties // on the browser page js = js.Replace(",]", "]; document.write(properties.join(' '));"); // set the proper content type so that browser can handle properly // the reply as a javascript source code. this.Response.ContentType = "text/javascript"; this.Response.Write(js); } #region GPL (c) 2009,2019 by DBJ -- GIF (aka: "Generic Iteration Framework") System.Collections.IList iterate_over (object host_, System.Type what_) { return meta_maker(what_)(host_); } delegate System.Collections.IList list_maker(object host_); list_maker meta_maker( System.Type what_ ) { if( what_.Equals( typeof(System.Reflection.PropertyInfo))) return make_list_of_properties; if (what_.Equals(typeof(System.Reflection.MethodInfo))) return make_list_of_methods; throw new System.ApplicationException( "Type [" + what_.Name + "] not handled by meta_maker()" ); } System.Collections.Generic.List make_list_of_properties(object host_) { return new System.Collections.Generic.List ( host_.GetType().GetProperties() ) ; } System.Collections.Generic.List make_list_of_methods(object host_) { return new System.Collections.Generic.List (host_.GetType().GetMethods()); } #endregion } |
In essence above is a generic solution with a minimal amount of C# generics. And with an use of System.Type
explicitly, instead of wrestling with C# generics, which are using it implicitly. It seems to me, that in the C# context, the amount of genericity is actually restricted by the restrictions of C# generics concepts. For example like an requirement for an obligatory default constructor. Or like completely redundant concept of “constraints” which are apparently invented to “enforce the type safety” ? Why actually, is a very moot point.
I think, legacy C#, posed a big problem when generics have been conceived and developed. It seems to me, in order to solve these problems, Constraints have been invented. Together with a story on why are they actually good for you. While in reality “constraints”, at least for me, in C# are nothing else but scaffolding that helps the C# compiler to create concrete type instances of generic classes.
Because it can not deduce what it needs, on its own, in order to be able to create them. This is very serious drawback for C# which is already rich in non obvious “features” which otherwise can be described as “design flaws”.
Ok, back to my solution above. The ‘Maker’ (meta_maker above) pattern, is a little brother of the Factory Pattern. Sometimes it is called “Factory Method”. This indicates it is usually implemented in a single function. I dare to think, that realistically, this particular solution can be extended to achieve desired level of genericity and reusability. And to have a maneuvring space for extending this Proof Of Concept, and this design, into the fully configurable solution.