12

我有一个有趣的问题。我需要动态包装静态类。即返回一个非静态实例给我的调用者。例如:

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

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

注意:很遗憾,我不能使用动态对象。

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

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

Edit2:另一个选择是使用 Emit 将一个空的构造函数注入到类型中,再次使用指针吗?这甚至可以在标记为静态的类型上实现吗?静态关键字是否进入 IL?

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

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

谢谢大家。

4

4 回答 4

2

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

你需要这样的东西:

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;
        }
    }
}

希望它有效。

于 2011-05-31T10:51:02.513 回答
1

因此,假设我们使用“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);
}

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

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

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

于 2011-05-31T18:03:42.460 回答
1

好的,我想出的解决方案如下,并且是通过阅读和研究Einar 的博客文章,他在上面作为评论发布的。谢谢艾纳尔。

但我想我会在这里发布我的完整代码解决方案,以防将来对某人有所帮助:

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);
    }
  }
}
于 2011-06-01T07:38:47.923 回答
1

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

您基本上可以完全按照博客文章中的方式进行操作,只是您不需要将“this”引用加载到堆栈中(因为您正在执行静态方法调用)。

于 2011-06-02T15:39:14.733 回答