Obtenga un tipo genérico usando Mono embedded
mono-embedding (1)
En muchos casos, un enigma de incrustación mono se puede resolver utilizando un método de ayuda administrado. Este es el enfoque utilizado aquí.
Entonces tenemos:
Un método de ayuda administrada que acepta una definición de tipo genérico y una matriz de tipos de parámetros genéricos.
Un método de cliente que acepta un nombre de definición de tipo genérico (p. Ej .: System.Collections.Generic.List`1), una imagen de ensamblado que contiene el tipo (o utiliza un nombre de Ensamblado) y un objeto del tipo de parámetro genérico requerido. Recuperamos el monoType subyacente para el objeto.
Tenga en cuenta que cuando se pasa información de tipo en la capa administrada debe ser una instancia de MonoReflectionType como se obtiene de mono_type_get_object ().
El método de ayuda administrada es trivial y realiza la instanciación real:
public static object CreateInstanceOfGenericType(Type genericTypeDefinition, Type[] parms)
{
// construct type from definition
Type constructedType = genericTypeDefinition.MakeGenericType(parms);
// create instance of constructed type
object obj = Activator.CreateInstance(constructedType);
return obj;
}
El código auxiliar se llama, en este caso, desde Objective-C:
+ (id)createInstanceOfGenericTypeDefinition:(char *)genericTypeDefinitionName monoImage:(MonoImage *)monoImage itemObject:(id)itemObject
{
// get the contained item monoType
MonoType *monoType = [DBType monoTypeForMonoObject:[itemObject monoObject]];
MonoReflectionType *monoReflectionType = mono_type_get_object([DBManagedEnvironment currentDomain], monoType);
// build a System.Array of item types
DBManagedObject *argType = [[DBManagedObject alloc] initWithMonoObject:(MonoObject *)monoReflectionType];
NSArray *argTypes = @[argType];
DBSystem_Array *dbsAargTypes = [argTypes dbsArrayWithTypeName:@"System.Type"];
// get the generic type definition
//
// Retrieves a MonoType from given name. If the name is not fully qualified,
// it defaults to get the type from the image or, if image is NULL or loading
// from it fails, uses corlib.
// This is the embedded equivalent of System.Type.GetType();
MonoType *monoGenericTypeDefinition = mono_reflection_type_from_name(genericTypeDefinitionName, monoImage);
// create instance using helper method
MonoMethod *helperMethod = [DBManagedEnvironment dubrovnikMonoMethodWithName:"CreateInstanceOfGenericType" className:"Dubrovnik.FrameworkHelper.GenericHelper" argCount:2];
void *hargs [2];
hargs[0] = mono_type_get_object([DBManagedEnvironment currentDomain], monoGenericTypeDefinition);
hargs[1] = [dbsAargTypes monoArray]; // a monoArray *
MonoObject *monoException = NULL;
MonoObject *monoObject = mono_runtime_invoke(helperMethod, NULL, hargs, &monoException);
if (monoException) NSRaiseExceptionFromMonoException(monoException);
id object = [System_Object subclassObjectWithMonoObject:monoObject];
return object;
}
Para ver el código completo, ver Dubrovnik en Github
¿Cómo puedo crear un objeto genérico List <String> usando llamadas integradas mono? Puedo obtener el MonoClass de List:
MonoClass* list = mono_class_from_name(mscorlibimage,
"System.Collections.Generic", "List`1");
y veo en los documentos que hay
mono_class_from_generic_parameter(MonoGenericParam*...)
pero no tengo idea de dónde y cómo obtener el MonoGenericParam. ¿O quizás necesito construir un nombre válido para mono_class_from_name? Creo que esto puede ser un poco más lento, pero lo aceptaría por ahora. Lo intenté
MonoClass* list = mono_class_from_name(mscorlib::get().image, "System.Collections.Generic", "List`1[System.String]");
pero sin suerte.
ACTUALIZAR:
OK, encontré la manera. Todavía me gustaría ver si hay una forma oficial de hacer las cosas, ya que este truco me parece demasiado sucio.
Básicamente busqué fuentes genéricas en mono sources y encontré mono_class_bind_generic_parameters (vea https://raw.github.com/mono/mono/master/mono/metadata/reflection.c ). Tuve que vincular a libmono-2.0.a además de .so para usarlo. Pero funcionó:
extern "C" MonoClass*
mono_class_bind_generic_parameters(MonoClass *klass,
int type_argc, MonoType **types, bool is_dynamic);
MonoClass* list = mono_class_from_name(mscorlib::get().image,
"System.Collections.Generic", "List`1");
MonoClass* strcls = mono_class_from_name(mscorlib::get().image, "System", "String");
printf("str class: %p/n", strcls);
MonoType* strtype = mono_class_get_type(strcls);
printf("str type: %p/n", strtype);
MonoType* types[1];
types[0] = strtype;
list = mono_class_bind_generic_parameters(list, 1, types, false);
printf("list[string] class: %p/n", list);
MonoObject* obj = mono_object_new(domain, list);
printf("list[string] created: %p/n", obj);
Supongo que puedo tomar las fuentes (ACTUALIZAR: poco) de estos métodos y volver a implementarlos (analizan los metadatos, etc.), si no quiero vincularlos con .a, pero me pregunto si hay una manera más simple. Los mono docs simplemente no responden nada, como solían hacerlo.
ACTUALIZACIÓN: encontró este hilo: http://mono.1490590.n4.nabble.com/Embedded-API-Method-signature-not-found-with-generic-parameter-td4660157.html que parece decir que no existe API incrustada por lo que quiero (es decir, no se molestan en exponer mono_class_bind_generic_parameters). ¿Alguien puede probar que es correcto? Con ese método, por cierto, obtengo MonoReflectionType * y no hay forma de recuperar MonoType * de él, mientras que es tan fácil obtener -> type de la estructura, que es interno y el acceso a través de funciones para él es interno. Mono Embedded debería llamarse "Mono Internal" en su lugar.
ACTUALIZACIÓN: otro método es hackear mono_class_inflate_generic_type usando copia de estructuras internas:
struct _MonoGenericInst {
uint32_t id; /* unique ID for debugging */
uint32_t type_argc : 22; /* number of type arguments */
uint32_t is_open : 1; /* if this is an open type */
MonoType *type_argv [1];
};
struct _MonoGenericContext {
/* The instantiation corresponding to the class generic parameters */
MonoGenericInst *class_inst;
/* The instantiation corresponding to the method generic parameters */
void *method_inst;
};
_MonoGenericInst clsctx;
clsctx.type_argc = 1;
clsctx.is_open = 0;
clsctx.type_argv[0] = mono_class_get_type(System::String::_SClass());
MonoGenericContext ctx;
ctx.method_inst = 0;
ctx.class_inst = &clsctx;
MonoType* lt = mono_class_inflate_generic_type(
mono_class_get_type(System::Collections::Generic::List<System::String>::_SClass()),
&ctx);
Esto no requiere un enlace estático a .a, pero es incluso peor. Y mono_class_inflate_generic_type está marcado como DEPRECATED - entonces, si esto está obsoleto, ¿ cuál es el más moderno?