How to wrap a static class in a non-static instance object (dynamically)
Asked Answered
I

4

13

I have an interesting problem. I need to wrap static classes dynamically. I.e. return a non-static instance to my callers. e.g.:

public object CreateInstance(string className) {
  Type t = assembly.GetType(className);
  if (IsStatic(t)) {
    return CreateStaticWrapper(t);
  } else {
    return Activator.CreateInstance(t);
  }
}

So what I need is pointers as to how to implement CreateStaticWrapper.

Note: I cannot use Dynamic objects unfortunately.

So what are my options? I'm not that keen on learning IL generation? If IL generation (Reflection.Emit, or is there other ways now?) is the way to go does anyone have pointers there?

Edit: It's important to note that I can return a Dictionary of Delegates. So I could use Delegate.CreateDelegate for this but I can't seem to work out how to handle overloaded methods and Generic methods.

Edit2: Another option would be to inject an empty constructor into the type using Emit, again any pointers? Is this even possible on a type marked as static? Does the static keyword make it into the IL?

Edit3: For a bit of context, I'm passing this to a javascript environment see: my project. So I would like to be able to (in JavaScript):

var fileHelper = .create('System.IO.File');
if (fileHelper.Exists(fileName)) { fileHelper.Delete(fileName); }

Thanks All.

Improbity answered 31/5, 2011 at 7:53 Comment(10)
Is your goal to to create a copy of the content of the static class? Is there a non static equivalent of the static class (a non static class with the same properties as the static class?Shred
What should the wrapper look like? Proxy instance members for corresponding static members on the original class?Kei
I've edited (3) for a bit of context. Basically I'm passing this static class to a javascript environment. So yes the proxy needs to have the same signature. I would like to be able to do (in javascript): var fs = .create('System.IO.File'); fs.Exists('filename');Improbity
I'd say go for IL generation. Creating a proxy is a pretty simple scenario. I actually wrote a blog post about it: einarwh.posterous.com/patching-polymorphic-pain-at-runtime. The scenario is different, but the solution almost identical.Kei
This sounds like you are trying to do mocking. Perhaps one of those libraries provides what you are looking for.Doge
@Einar, your post looks good, its just once I get to stuff like: OpCodes.Ldarg_0 I get nervous about copy/paste. I like to know what I'm doing, so looks like c#4 in a nutshell is comming out of the shelf again. I'll revert once I learn and try your approach.Improbity
@leppie, what mocking framework mocks static types? I don't want to pay for TypeMock for an OS project, I really also don't want to rely on another dll for my projectImprobity
@gatapia: Anyone that supports IL rewriting (iow using the CLR Profiler API). Not sure if there are free options though.Doge
@Einar: Your post led to my solution, would you like to post as an answer so I can 'check'.Improbity
@Improbity Thanks, I've added the comment as an answer below.Kei
K
2

I'd say go for IL generation. Creating a proxy is a pretty simple scenario. I actually wrote a blog post about it: einarwh.posterous.com/patching-polymorphic-pain-at-runtime. The scenario is different, but the solution almost identical.

You can basically do exactly as in the blog post, except you don't need to load the 'this' reference onto the stack (since you're doing static method calls).

Kei answered 2/6, 2011 at 15:39 Comment(0)
E
3

Try creating a wrapper class which inherits from System.Dynamic.DynamicObject. In the wrapper class, use reflection to call the methods of the static class.

You need something like this:

public class StaticWrapper<T> : System.Dynamic.DynamicObject
{
    private static readonly Type t = typeof(T);
    public static int MyProperty { get; set; }
    public override bool TryInvokeMember(System.Dynamic.InvokeMemberBinder binder, object[] args, out object result)
    {
        try
        {
            result = t.InvokeMember(binder.Name, BindingFlags.Static | BindingFlags.Public, null, null, args);
            return true;
        }
        catch
        {
            result = null;
            return false;
        }
    }
    public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result)
    {
        try
        {
            var p = t.GetProperty(binder.Name);
            if (p != null)
                result = p.GetValue(null, null);
            else
            {
                var f = t.GetField(binder.Name);
                if (f != null) result = f.GetValue(null);
                else { result = null; return false; }
            }
            return true;
        }
        catch
        {
            result = null;
            return false;
        }
    }
    public override bool TrySetMember(System.Dynamic.SetMemberBinder binder, object value)
    {
        try
        {
            var p = t.GetProperty(binder.Name);
            if (p != null)
                p.SetValue(null, value, null);
            else
            {
                var f = t.GetField(binder.Name);
                if (f != null) f.SetValue(null, value);
                else return false;
            }
            return true;
        }
        catch (SystemException)
        {
            return false;
        }
    }
}

Hope it works.

Emulous answered 31/5, 2011 at 10:51 Comment(2)
Unfortunately the whole family Dynamic objects is not an option to me. That was also my first intuitive choice but +1 for the great response.Improbity
In InvokeMember Add | BindingFlags.InvokeMethod to Fix runtime error. Static class can't as generic type param e.g. Console.Spelunker
I
2

Ok, well the solution I've come up with is as follows and was found reading through and studying Einar's blog post which he posted as a comment above. Thanks Einar.

But I thought I'd post my full code solution here in case it may help someone in the future:

using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;

namespace js.net.jish.Command
{
  public class StaticTypeWrapper
  {
    private readonly Type staticType;

    public StaticTypeWrapper(Type staticType)
    {
      this.staticType = staticType;
    }

    public object CreateWrapper()
    {
      string ns = staticType.Assembly.FullName;      
      ModuleBuilder moduleBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(ns), AssemblyBuilderAccess.Run).DefineDynamicModule(ns); 
      TypeBuilder wrapperBuilder = moduleBuilder.DefineType(staticType.FullName, TypeAttributes.Public, null, new Type[0]);  
      foreach (MethodInfo method in staticType.GetMethods().Where(mi => !mi.Name.Equals("GetType")))
      {
        CreateProxyMethod(wrapperBuilder, method);
      }
      Type wrapperType = wrapperBuilder.CreateType();
      object instance = Activator.CreateInstance(wrapperType);
      return instance;
    }

    private void CreateProxyMethod(TypeBuilder wrapperBuilder, MethodInfo method)
    {
      var parameters = method.GetParameters();

      var methodBuilder = wrapperBuilder.DefineMethod(method.Name, MethodAttributes.Public | MethodAttributes.Virtual, method.ReturnType, parameters.Select(p => p.ParameterType).ToArray());
      var gen = methodBuilder.GetILGenerator();

      for (int i = 1; i < parameters.Length + 1; i++)
      {
        gen.Emit(OpCodes.Ldarg, i); 
      }
      gen.Emit(OpCodes.Call, method); 
      gen.Emit(OpCodes.Ret);
    }
  }
}
Improbity answered 1/6, 2011 at 7:38 Comment(0)
K
2

I'd say go for IL generation. Creating a proxy is a pretty simple scenario. I actually wrote a blog post about it: einarwh.posterous.com/patching-polymorphic-pain-at-runtime. The scenario is different, but the solution almost identical.

You can basically do exactly as in the blog post, except you don't need to load the 'this' reference onto the stack (since you're doing static method calls).

Kei answered 2/6, 2011 at 15:39 Comment(0)
W
1

So, say that we play around with the "Delegate.CreateDelegate" way. And let's see if we can get more details about your other issues after that... Let's start with:

public static object Generate(Type t)
{
    if(IsStatic(t))
    {
        var dictionary = new Dictionary<string, Delegate>();
        foreach (var methodInfo in t.GetMethods())
        {
            var d = Delegate.CreateDelegate(t, methodInfo);
            dictionary[methodInfo.Name] = d;
        }
        return dictionary;
    }
    return Activator.CreateInstance(t);
}

Static classes are 'sealed' and can thus not be inherited. So I don't see what you mean by 'overloaded'. For generic methods, we need to invoke the methodInfo.MakeGenericMethod(...) before adding it to our dictionary. But then you would need to know the type beforehand, which I guess you don't... Alternatively, you can do something like:

...
if (methodInfo.IsGenericMethod)
{
    d = new Func<MethodInfo, Type[], Delegate>(
        (method, types) =>
        Delegate.CreateDelegate(
            method.DeclaringType, method.MakeGenericMethod(types)));
}
dictionary[methodInfo.Name] = d;
...

That would give you a delegate that would take a type array (the generic type parameters), and produce a working delegate from that.

Windlass answered 31/5, 2011 at 18:3 Comment(1)
I really like the way you handle generics and I will use that, however that does not help me with overriden members. I.e. Having System.IO.Directory.Delete(dirName) and Delete(dirName, recursive) means I can only register one 'Delete'. What I'm doing now is registering only a single Delete with an Array of args and then dynamically delegating to the correct implementation. This has the ugliness of changing the signature in JavaScript. So now in JS I need to do this: Directory.Delete([dirname]). Whilst all other calls are true to their original interface.Improbity

© 2022 - 2024 — McMap. All rights reserved.