2011-05-31 142 views
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中?

编辑3:对于一些上下文,我将它传递给javascript环境,请参阅:my project。所以我希望能够(在JavaScript中):

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

谢谢所有。

+0

您的目标是创建一个静态类的内容的副本?是否有静态类的非静态等价物(与静态类具有相同属性的非静态类?) – k3b 2011-05-31 08:30:10

+0

包装物应该是什么样子?原始类中相应静态成员的代理实例成员? – Einar 2011-05-31 08:52:33

+0

我已编辑( 3)有点上下文,基本上我把这个静态类传递给一个javascript环境,所以是的代理需要有相同的签名,我希望能够做到(在JavaScript中):var fs = .create ('System.IO.File'); fs.Exists('filename'); – gatapia 2011-05-31 08:57:15

回答

1

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

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

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

希望工程。

+0

不幸的是整个家庭的动态对象是[不是我的选择](http://javascriptdotnet.codeplex.com/discussions/235767)。这也是我的第一个直觉选择,但是对于这个伟大的回应来说是+1 – gatapia 2011-05-31 20:23:18

+0

在'InvokeMember'中添加'| BindingFlags.InvokeMethod'修复运行时错误。静态类不能作为泛型类型参数,例如安慰。 – IlPADlI 2015-01-10 13:28:26

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

这将使你的委托,将采取一个类型数组(该泛型类型参数),并从中产生一个工作委托。

+0

我真的很喜欢你处理泛型的方式,我会使用它,但是这并不能帮助我们重写成员。即拥有System.IO.Directory.Delete(dirName)和Delete(dirName,递归)意味着我只能注册一个“删除”。我现在所做的只是注册一个带有一个args数组的Delete,然后动态委派给正确的实现。这具有在JavaScript中更改签名的丑陋性。所以现在在JS中我需要这样做:'Directory.Delete([dirname])'。所有其他呼叫都是真实的接口。 – gatapia 2011-05-31 20:27:27

1

好吧,我提出的解决方案如下,并发现阅读和研究Einar's blog post,他发布了上面的评论。谢谢Einar。

但我想我会后我的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); 
    } 
    } 
}