2026 Q1 Update — Making This Code Work Today
24 September 2009

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 will show the list of property values of the Request.Browser (a .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 that 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 kinds 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 the ASP.NET runtime, for each browser request by looking into HTTP Server variables set after the connection with the browser.
The 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 realised this is a time for (yet another) Factory pattern implementation. And then, I confidently went along to do this aspx, with the help of C# generics. Just to quickly realise 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 transform C++ template concepts into C#. It is actually impossible, and it requires a 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 the use of System.Type explicit code, instead of wrestling with C# generics, which are used 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 a requirement for an obligatory default constructor. Or like a 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 were conceived and developed. It seems to me, to solve these problems, Constraints have been invented. Together with a story on why they are actually good for you. While in reality, “constraints”, at least for me, in C# are nothing 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, to be able to create them. This is a 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 the desired level of genericity and reusability. And to have a manoeuvring space for extending this Proof Of Concept, and this design, into a fully configurable solution.
2026 Q1 Update — Making This Code Work Today
This code was written in 2009 for classic ASP.NET WebForms, targeting .NET Framework 2.0–4.x.
The central dependency is System.Web.UI.Page and System.Web.HttpBrowserCapabilities —
both belonging to the System.Web namespace that Microsoft explicitly dropped from .NET Core and all
subsequent .NET versions (5, 6, 7, 8, 9).
There are two practical paths forward, depending on intent.
Option 1 — Run As-Is on .NET Framework 4.8 (Windows, zero code change)
If the goal is preservation or a quick demo, .NET Framework 4.8 is still fully supported
(Microsoft extended support until at least 2029) and the code compiles and runs without modification.
- Install **.NET Framework 4.8 targeting pack** via Visual Studio 2022 Installer → Individual components.
- Create a new ASP.NET Web Forms Application project targeting .NET Framework 4.8.
- Drop the
.aspx+.aspx.csfiles in. No code changes needed. - Host on IIS or IIS Express on Windows.
(
mcr.microsoft.com/dotnet/framework/aspnet:4.8) but the image is ~8 GB and Linux containersare not an option for
System.Web.Option 2 — Port to .NET 8/9 Minimal API (cross-platform, containerisable)
The original design pattern — factory-method dispatch via a delegate keyed on System.Type
— compiles in .NET 8 without changes. Only the System.Web surface needs replacing.
The reflection logic and the meta_maker / iterate_over pattern survives intact.
The equivalent endpoint in a .NET 8 Minimal API project:
|
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 |
// Program.cs — .NET 8 minimal API equivalent of the original ASPX var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/meta", (HttpContext ctx) => { // HttpRequest replaces System.Web.HttpBrowserCapabilities as the reflection host var props = MetaMaker.ListProperties(ctx.Request) .Select(pi => MetaMaker.PropValueJson(ctx.Request, pi)); string js = $"var properties = ['{ctx.Request.GetType().Name}', " + string.Join(", ", props) + "]; document.write(properties.join('<br>'));"; return Results.Text(js, "text/javascript"); }); app.Run(); // ── GIF (Generic Iteration Framework) — ported from 2009 original ──────────── public static class MetaMaker { // delegate signature preserved from original public delegate IList ListMaker(object host); public static ListMaker GetMaker(Type what) { if (what == typeof(System.Reflection.PropertyInfo)) return ListProperties; if (what == typeof(System.Reflection.MethodInfo)) return ListMethods; throw new ApplicationException($"Type [{what.Name}] not handled by GetMaker()"); } public static IList ListProperties(object host) => [.. host.GetType().GetProperties()]; public static IList ListMethods(object host) => [.. host.GetType().GetMethods()]; public static string PropValueJson(object host, System.Reflection.PropertyInfo pi) { string val; try { val = !pi.CanRead ? "CANNOT READ" : pi.PropertyType.IsNotPublic ? "NOT PUBLIC" : pi.IsSpecialName ? "SPECIAL NAME" : pi.GetValue(host)?.ToString() ?? "null"; } catch (Exception x) { val = x.Message; } return $"'{{ \"{pi.Name}\" : \"{val}\" }}'"; } } |
The delegate System.Collections.IList list_maker(object host_) from the original is preserved.
The meta_maker factory dispatch is renamed GetMaker to follow current C# naming conventions,
But the structure is identical.
Comparison
| Criterion | .NET Framework 4.8 | .NET 8 Minimal API |
|---|---|---|
| Code changes | None | ~30 minutes |
| Platform | Windows only | Linux / Windows / macOS |
| Linux container | No | Yes (mcr.microsoft.com/dotnet/aspnet:8.0) |
| Long-term support | Until ~2029 | Until Nov 2026 (.NET 8 LTS) |
| Original pattern preserved | Yes (verbatim) | Yes (structurally) |
Note on C# Generics — Still True in 2026
The 2009 commentary on C# generics versus C++ templates remains accurate. The constraint system
has been marginally extended (e.g. where T : allows ref struct in C# 13), but the fundamental
limitation — that the compiler cannot deduce what it needs without explicit constraints — is
unchanged. The original solution’s choice to avoid generics in favour of explicit System.Type
Dispatch and delegates are still a pragmatic and valid design decision in 2026.
— Updated 2026 Q1