How to I create a generic List<String> object using mono embedded calls? I can get List's MonoClass:

MonoClass* list = mono_class_from_name(mscorlibimage,
     "System.Collections.Generic", "List`1");

and I see in docs that there's

mono_class_from_generic_parameter(MonoGenericParam*...)

but I have no idea where and how to get the MonoGenericParam. Or perhaps I need to construct a valid name for mono_class_from_name? I think this can be a bit slower but I'd accept that for now. I tried

MonoClass* list = mono_class_from_name(mscorlib::get().image, "System.Collections.Generic", "List`1[System.String]");

but no luck.

UPDATE:

OK I found a way. Still I'd like to see if there's an official way of doing thing, as this hack looks too dirty to me.

Basically I searched mono sources for generic methods and found mono_class_bind_generic_parameters (see https://raw.github.com/mono/mono/master/mono/metadata/reflection.c). I had to link to libmono-2.0.a in addition to .so to use it. But it worked:

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

I suppose I can take sources (UPDATE: hardly so) of these methods and reimplement them (they parse metadata, etc) - if I don't want to link to .a - but I wonder if there's a simpler way. Mono docs just don't answer anything, as they use to.

UPDATE: found this thread: http://mono.1490590.n4.nabble.com/Embedded-API-Method-signature-not-found-with-generic-parameter-td4660157.html which seems to say that no embedded API exists for what I want (i.e. they do not bother to expose mono_class_bind_generic_parameters). Can someone prove that it's correct? With that method, by the way, I get MonoReflectionType* and no way to get back MonoType* from it - while it is as easy to as getting ->type from the structure - which is internal and access via functions to it is internal. Mono Embedded should be called "Mono Internal" instead.

UPDATE: another method is to hack mono_class_inflate_generic_type using copy of internal structures:

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

This doesn't require static link to .a but is even a worse hack. And mono_class_inflate_generic_type is marked as DEPRECATED - so, if this is deprecated, then which is the modern one?

有帮助吗?

解决方案

In many cases a mono embedding conundrum can be resolved by using a managed helper method. This is the approach used here.

So we have:

  1. A managed helper method that accepts a generic type definition and an array of generic parameter types.

  2. A client method that accepts a generic type definition name (e.g.: System.Collections.Generic.List`1), an assembly image that contains the type (or use an Assembly Qualified name) and an object of the required generic parameter type. We retrieve the underlying monoType for the object.

Note that when passing type info into the managed layer it has to be an instance of MonoReflectionType as obtained from mono_type_get_object().

The managed helper method is trivial and does the actual instantiation:

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

The helper code is called, in this case, from 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;
}

For the complete code see Dubrovnik on Github

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top