如何在非静态实例对象中包装静态类(动态)

时间:2022-09-01 17:10:55

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.

所以我需要的是关于如何实现CreateStaticWrapper的指针。

Note: I cannot use Dynamic objects unfortunately.

注意:遗憾的是我不能使用Dynamic对象。

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?

那么我的选择是什么?我不是那么热衷于学习IL一代吗?如果IL生成(Reflection.Emit,或者现在还有其他方法吗?)是要走的路吗?有没有人指点那里?

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.

编辑:重要的是要注意我可以返回代表词典。所以我可以使用Delegate.CreateDelegate,但我似乎无法解决如何处理重载方法和通用方法。

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?

Edit2:另一种选择是使用Emit将空构造函数注入到类型中,再次指向任何指针?甚至可以在标记为静态的类型上进行此操作吗? static关键字是否会进入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):

Edit3:对于一些上下文,我将它传递给javascript环境,请参阅:我的项目。所以我希望能够(在JavaScript中):

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

Thanks All.

4 个解决方案

#1


1  

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.

我会说去生成IL。创建代理是一个非常简单的方案。我实际上写了一篇关于它的博文:einarwh.posterous.com/patching-polymorphic-pain-at-runtime。情景不同,但解决方案几乎完全相同。

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).

您基本上可以像在博客文章中那样完成,除非您不需要将“this”引用加载到堆栈中(因为您正在进行静态方法调用)。

#2


2  

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.

尝试创建一个继承自System.Dynamic.DynamicObject的包装类。在包装器类中,使用反射来调用静态类的方法。

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.

希望它有效。

#3


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:

所以,假设我们使用“Delegate.CreateDelegate”方式。让我们看看在此之后我们是否可以获得有关您其他问题的更多详细信息...让我们从以下开始:

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:

静态类是“密封的”,因此不能被继承。所以我看不出'重载'是什么意思。对于泛型方法,我们需要在将其添加到字典之前调用methodInfo.MakeGenericMethod(...)。但是你需要事先知道这个类型,我猜你不会...或者,你可以这样做:

...
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.

这将为您提供一个将采用类型数组(泛型类型参数)的委托,并从中生成一个工作委托。

#4


1  

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.

好吧,我提出的解决方案如下,并且发现阅读并研究Einar的博客文章,他在上面发表评论。谢谢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);
    }
  }
}

#1


1  

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.

我会说去生成IL。创建代理是一个非常简单的方案。我实际上写了一篇关于它的博文:einarwh.posterous.com/patching-polymorphic-pain-at-runtime。情景不同,但解决方案几乎完全相同。

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).

您基本上可以像在博客文章中那样完成,除非您不需要将“this”引用加载到堆栈中(因为您正在进行静态方法调用)。

#2


2  

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.

尝试创建一个继承自System.Dynamic.DynamicObject的包装类。在包装器类中,使用反射来调用静态类的方法。

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.

希望它有效。

#3


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:

所以,假设我们使用“Delegate.CreateDelegate”方式。让我们看看在此之后我们是否可以获得有关您其他问题的更多详细信息...让我们从以下开始:

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:

静态类是“密封的”,因此不能被继承。所以我看不出'重载'是什么意思。对于泛型方法,我们需要在将其添加到字典之前调用methodInfo.MakeGenericMethod(...)。但是你需要事先知道这个类型,我猜你不会...或者,你可以这样做:

...
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.

这将为您提供一个将采用类型数组(泛型类型参数)的委托,并从中生成一个工作委托。

#4


1  

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.

好吧,我提出的解决方案如下,并且发现阅读并研究Einar的博客文章,他在上面发表评论。谢谢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);
    }
  }
}