【问题标题】:How to wrap a static class in a non-static instance object (dynamically)如何将静态类包装在非静态实例对象中(动态)
【发布时间】:2011-09-05 06:51:46
【问题描述】:

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

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 环境,请参阅:my project。所以我希望能够(在 JavaScript 中):

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

谢谢大家。

【问题讨论】:

  • 您的目标是创建静态类内容的副本吗?是否有静态类的非静态等价物(与静态类具有相同属性的非静态类?
  • 包装器应该是什么样的?原类上对应静态成员的代理实例成员?
  • 我已经编辑了 (3) 以获取一些上下文。基本上我将这个静态类传递给 javascript 环境。所以是的,代理需要具有相同的签名。我希望能够做到(在 javascript 中): var fs = .create('System.IO.File'); fs.Exists('文件名');
  • 我会说去 IL 生成。创建代理是一个非常简单的场景。我实际上写了一篇关于它的博客文章:einarwh.posterous.com/patching-polymorphic-pain-at-runtime。场景不同,但解决方案几乎相同。
  • 这听起来像是你在尝试做模拟。也许其中一个库提供了您正在寻找的内容。

标签: c# .net instance non-static static-class


【解决方案1】:

尝试创建一个继承自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;
        }
    }
}

希望它有效。

【讨论】:

  • 不幸的是全家动态对象是not an option to me。这也是我的第一个直观选择,但 +1 是因为反应很好。
  • InvokeMember 中添加| BindingFlags.InvokeMethod 以修复运行时错误。静态类不能作为泛型类型参数,例如控制台。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    所以,假设我们使用“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;
    ...
    

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

    【讨论】:

    • 我真的很喜欢你处理泛型的方式,我会使用它,但是这对我覆盖的成员没有帮助。 IE。拥有 System.IO.Directory.Delete(dirName) 和 Delete(dirName, recursive) 意味着我只能注册一个“删除”。我现在正在做的是只注册一个带有 args 数组的删除,然后动态委派给正确的实现。这有在 JavaScript 中更改签名的丑陋。所以现在在 JS 中我需要这样做:Directory.Delete([dirname])。而所有其他调用都符合其原始界面。
    【解决方案4】:

    好的,我想出的解决方案如下,并通过阅读和研究Einar's blog post 他在上面作为评论发布。谢谢艾纳尔。

    但我想我会在这里发布我的full code solution,以防将来可能对某人有所帮助:

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

    【讨论】:

      猜你喜欢
      • 2014-01-17
      • 1970-01-01
      • 1970-01-01
      • 2012-09-23
      • 2012-02-14
      • 1970-01-01
      • 1970-01-01
      • 2013-02-26
      相关资源
      最近更新 更多