Almost immediately following yesterday's post I got to thinking about supporting static classes / methods. I see static classes all the time where the class represents a set of related-but-operationally-distinct methods, often utilities. The post yesterday really had two aspects about it: 1) the retrieval / extraction and the hosting of an assembly embedded as a resource within another assembly and 2) the creation of a proxy type by wrapping the logic surrounding using Reflection to dig into the guts of the referenced object in a base class.
The ante is raised (only slightly) when dealing with static objects. Static objects have no instance methods and therefore have no inheritance support and no constructors (the static .ctor is really a type initializer and it's quite the same thing). Our simple example yesterday defines an abstract base class (ProxyClassBase) that encapsulates the logic for creating the proxy instance and invoking the methods within it.
If we were to mimic the structure of the reference object (the object from the extracted assembly) in our proxy class we would be unable to inherit ProxyClassBase because our proxy would be static. Allow me to flesh out an example illustrating this:
Example 01: Static Proxy
For this to work, I'd have to create a new class. Let's name it StaticProxyClass. This class would, in large measure, be quite similar to the ProxyClassBase base type we defined yesterday with a few distinctions: 1) we wouldn't need an Instance (all methods are static) and 2) we can't have a protected .ctor (our proxy would be static and would be unable to inherit the class).
Our type might resemble the following:
namespace HostAsm { internal class StaticProxyClass { internal StaticProxyClass(string assemblyName, string typeName) { _type = AssemblyLoader.GetType(assemblyName, typeName); } private readonly Type _type; internal void InvokePublicVoidMethod(string methodName, object[] parameters) { Type[] types = getTypes(parameters); MethodInfo method = _type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Standard, types, null); method.Invoke(null, parameters); } private static Type[] getTypes(object[] parameters) { Type[] result; if ( null == parameters || 0 == parameters.Length ) result = Type.EmptyTypes; else { result = new Type[parameters.Length]; for ( int i = 0; i < parameters.Length; i++ ) { result[i] = parameters[i].GetType(); } } return result; } } }
Note that I've included a method (getTypes) that I didn't include in yesterday's example but may well belong in the ProxyClassBase type as well. This method will assist in resolving overloaded methods (methods with the same name but with differing parameters).
Our static proxy class would effectively wrap an instance of this type and delegate calls to the reference object through it.
namespace HostAsm { internal static class CustomStaticClass { static CustomStaticClass() { _proxy = new StaticProxyClass("EmbeddedAsm.dll", "EmbeddedAsm.CustomStaticClass"); } private static readonly StaticProxyClass _proxy; internal static void StaticMethod(string data) { _proxy.InvokePublicVoidMethod("StaticMethod", new object[] { data }); } }}
This is all well and good and it works perfectly well. There's something missing, however, and a few things bug me about it.
First of all, it bugs me most that we'd end up having to duplicate (in large part) the functionality in one class (ProxyClassBase) in another (StaticProxyClass). Secondly, we're omitting one major part of the discussion: objects that have both instance methods and static methods. To me, this is the deal breaker.
To support this and to leverage our ProxyClassBase type even for static methods, I'd do away with the StaticProxyClass and add the methods therein to the ProxyClassBase. This will necessitate that our proxy type for static types be non-static, but we can pseudo-simulate that by making its .ctor private. For these, we will have to encapsulate the actual proxy within our proxy type. In fact, we'd have to take that one more level by creating delegate methods on our proxy class that direct the calls to the base type. I'd rather do this than muck with the scope of the base class's methods to keep my intentions pure with the base class.
Example 02: Enhancing the ProxyClassBase
The ProxyClassBase will get the functionality we just added to StaticProxyClass as well as a flag indicating whether the wrapped type is a static type.
using System;using System.Reflection;namespace HostAsm { internal abstract class ProxyClassBase { protected ProxyClassBase(string assemblyName, string typeName, bool isStatic) { ObjType = AssemblyLoader.GetType(assemblyName, typeName); Instance = isStatic ? null : Activator.CreateInstance(ObjType); } protected Type ObjType { get; private set; } protected object Instance { get; private set; } protected bool IsStatic { get; private set; } protected T InvokePublicMethod<T>(string methodName) { MethodInfo method = ObjType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.Standard, Type.EmptyTypes, null); return ( T )method.Invoke(Instance, null); } protected void InvokePublicStaticVoidMethod(string methodName, object[] parameters) { Type[] types = getTypes(parameters); MethodInfo method = ObjType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static, null, CallingConventions.Standard, types, null); method.Invoke(null, parameters); } private Type[] getTypes(object[] parameters) { Type[] result; if ( null == parameters || 0 == parameters.Length ) result = Type.EmptyTypes; else { result = new Type[parameters.Length]; for ( int i = 0; i < parameters.Length; i++ ) { result[i] = parameters[i].GetType(); } } return result; } }}
Then, to finish the puzzle, our proxy class that wraps the static type gets a face lift:
using System;namespace HostAsm { internal class CustomStaticClass : ProxyClassBase { private CustomStaticClass() : base("EmbeddedAsm.dll", "EmbeddedAsm.CustomStaticClass", true) { } private static readonly CustomClass2 _proxy = new CustomClass2(); internal static void StaticMethod(string data) { _proxy.invokePublicStaticVoidMethod("StaticMethod", new object[] { data }); } private void invokePublicStaticVoidMethod(string methodName, object[] parameters) { base.InvokePublicStaticVoidMethod(methodName, parameters); } }}
I've not given this approach a great deal of scrutiny. At first glance I can see it being somewhat fragile. If the base class changes you have to proliferate changes to the inheriting types. Then again, I don't see this base class like this changing much if at all except for new additions.
Powered by: newtelligence dasBlog 2.0.7226.0
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.
© Copyright 2010R. Aaron Zupancic
E-mail