2011-05-31 8 views
12

Tengo un problema interesante. Necesito envolver las clases estáticas dinámicamente. Es decir. devolver una instancia no estática a mis interlocutores. ej .:Cómo envolver una clase estática en un objeto instancia no estático (dinámicamente)

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

Así que lo que necesito es punteros en cuanto a la forma de aplicar CreateStaticWrapper.

Nota: Desafortunadamente, no puedo usar objetos dinámicos.

¿Cuáles son mis opciones? No estoy tan interesado en aprender IL generation? Si la generación IL (Reflection.Emit, ¿o hay otras formas ahora?) Es el camino a seguir, ¿alguien tiene punteros allí?

Editar: Es importante tener en cuenta que puedo devolver un Dictionary of Delegates. Así que podría usar Delegate.CreateDelegate para esto, pero parece que no puedo resolver cómo manejar métodos sobrecargados y métodos genéricos.

Edit2: Otra opción sería inyectar un constructor vacío en el tipo usando Emit, de nuevo cualquier puntero? ¿Esto es posible incluso en un tipo marcado como estático? ¿La palabra clave estática lo convierte en IL?

Edit3: Por un poco de contexto, estoy pasando esto a un entorno de JavaScript, vea: my project. Así que me gustaría poder (en JavaScript):

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

Thanks All.

+0

¿Su objetivo es crear una copia del contenido de la clase estática? ¿Existe un equivalente no estático de la clase estática? (Una clase no estática con las mismas propiedades que la clase estática? – k3b

+0

¿Cómo debería verse el contenedor? Miembros de instancia Proxy para los miembros estáticos correspondientes en la clase original? – Einar

+0

He editado (3) por un poco de contexto. Básicamente estoy pasando esta clase estática a un entorno de JavaScript. Así que sí, el proxy necesita tener la misma firma. Me gustaría poder hacerlo (en javascript): var fs = .create ('System.IO.File'); fs.Exists ('filename'); – gatapia

Respuesta

1

Yo diría que ir para la generación de IL. Crear un proxy es un escenario bastante simple. De hecho, escribí una publicación en el blog sobre esto: einarwh.posterous.com/patching-polymorphic-pain-at-runtime.El escenario es diferente, pero la solución es casi idéntica.

Básicamente puedes hacer exactamente lo mismo que en la publicación del blog, excepto que no necesitas cargar la referencia 'this' en la pila (ya que estás haciendo llamadas al método estático).

2

Intente crear una clase contenedora que herede de System.Dynamic.DynamicObject. En la clase contenedora, utilice la reflexión para llamar a los métodos de la clase estática.

se necesita algo como esto:

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

esperar que funcione.

+0

Lamentablemente, toda la familia de objetos dinámicos es [no una opción para mí] (http://javascriptdotnet.codeplex.com/discussions/235767). Esa fue también mi primera elección intuitiva, pero +1 para la gran respuesta. – gatapia

+0

En 'InvokeMember' Agregar' | BindingFlags.InvokeMethod' para corregir el error de tiempo de ejecución. La clase estática no puede, como un tipo genérico, param, e.g. Consola. – IlPADlI

1

Por lo tanto, supongamos que jugamos con la forma "Delegate.CreateDelegate". Y vamos a ver si podemos obtener más detalles acerca de sus otros problemas después de eso ... Vamos a empezar con:

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

clases estáticas son 'sellados' y por lo tanto no pueden ser heredados. Entonces no veo a qué te refieres con "sobrecargado". Para los métodos genéricos, debemos invocar el methodInfo.MakeGenericMethod(...) antes de agregarlo a nuestro diccionario. Pero entonces usted tendría que saber el tipo de antemano, lo que supongo que no ... Como alternativa, puede hacer algo como:

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

que le daría un delegado que tomaría un tipo array (la parámetros de tipo genérico), y producir un delegado de trabajo a partir de eso.

+0

Me gusta mucho la forma en que manejas los genéricos y la usaré, sin embargo, eso no me ayuda con los miembros anulados. Es decir. Tener System.IO.Directory.Delete (dirName) y Delete (dirName, recursive) significa que solo puedo registrar un 'Borrar'. Lo que estoy haciendo ahora es registrar solo una eliminación con una matriz de argumentos y luego delegar dinámicamente a la implementación correcta. Esto tiene la fealdad de cambiar la firma en JavaScript. Entonces ahora en JS necesito hacer esto: 'Directory.Delete ([dirname])'. Mientras que todas las demás llamadas son fieles a su interfaz original. – gatapia

1

Bien, la solución que he encontrado es la siguiente y fue encontrada leyendo y estudiando Einar's blog post que publicó como comentario arriba. Gracias Einar.

Pero yo pensé que había puesto mi full code solution aquí por si puede ayudar a alguien en el futuro:

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