C # Вызов и возврат объекта из статического метода в IL - PullRequest
2 голосов
/ 08 марта 2012

Это расширение предлагаемых решений здесь .Я создал статический метод, который возвращает мне объект.Моя цель - написать динамический метод для типа, который я определяю во время выполнения, чтобы вернуть мне объект, который возвращает этот статический метод.Мой код до сих пор:

 // type builder and other prep stuff removed for sake of space and reading

private void EmitReferenceMethodBody(Type returnType)
{
    MethodBuilder builder =
    typeBuilder.DefineMethod(
                    method.Name,
                    MethodAttributes.Virtual | MethodAttributes.Public,
                    method.CallingConvention,
                    method.ReturnType,
                    typeArray1);
    builder.InitLocals = true;
    ILGenerator gen = builder.GetILGenerator();
    MethodInfo getStoredObject = typeof(ObjectStore).GetMethod("GetStoredObject",                  BindingFlags.Public | BindingFlags.Static);        
    MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");            

    gen.Emit(OpCodes.Ldtoken, returnType);
    gen.Emit(OpCodes.Call, getTypeFromHandle);
    gen.Emit(OpCodes.Call, getStoredObject);
    gen.Emit(OpCodes.Ret);   
}

Обновленный код теперь вызывает метод, но, похоже, передает тип динамически созданного типа, а не переменную returnType.

Ответы [ 2 ]

4 голосов
/ 08 марта 2012

По крайней мере, одна проблема заключается в том, что вы помещаете ссылку "this" (OpCodes.Ldarg_0) в стек, даже если он никогда не извлекается (поскольку вы вызываете метод static ).Я бы попытался удалить эту строку и посмотреть, будет ли она вести себя лучше.

Другая проблема заключается в том, что вы передаете new Type[] { returnType } методу EmitCall.Это предназначено для необязательных аргументов (params), и я подозреваю, что ваш метод на самом деле не имеет никаких параметров.Следовательно, вам также следует удалить этот аргумент.

Редактировать:

На основе комментариев вы пытаетесь передать объект System.Type, статически известный методувы вызываете динамически.Это возможно, но вам нужно прыгнуть через пару обручей.

  1. Получить ссылку на MethodInfo для метода Type.GetTypeFromHandle:

    MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
    
  2. Используйте следующие строки IL, чтобы поместить ваш returnType в стек:

    gen.Emit(OpCodes.Ldtoken, returnType);
    gen.Emit(OpCodes.Call, getTypeFromHandle);
    

В итоге ваш код должен выглядеть следующим образом:

MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
gen.Emit(OpCodes.Ldtoken, returnType);
gen.Emit(OpCodes.Call, getTypeFromHandle);
gen.EmitCall(OpCodes.Call, getStoredObject);                
gen.Emit(OpCodes.Ret);

Переходное поведение стека этого кода:

  1. Вставьте RuntimeTypeHandle, соответствующий указанной ссылке Type, в стек с помощью Opcodes.Ldtoken.

  2. Invoke getTypeFromHandle, который выталкивает дескриптор типа из стека и помещает фактический System.Type в стек.

  3. Позвоните вашемустатический метод, который извлекает аргумент Type из стека и помещает возвращаемое значение вашего собственного метода в стек.

  4. Возврат из метода.

1 голос
/ 08 марта 2012

Деревья выражений могут быть лучшим решением здесь.Довольно просто создать Func<Type, object>, используя динамическую типизацию с помощью выражений.

ParameterExpression paramEx = Expression.Parameter(typeof(Type), "paramObject");
Expression callExpression = Expression.Call(typeof(ObjectStore), "GetStoredObject", null, paramEx);
Expression<Func<Type, object>> funcExpression = Expression.Lambda<Func<Type, object>>(callExpression, paramEx);
Func<Type, object> actualFunc = funcExpression.Compile();

Теперь, если для ObjectStore требуются общие параметры, вы должны заменить typeof(ObjectStore) на typeof(ObjectStore).MakeGenericType(returnType).Если для самой функции GetStoredObject требуются общие параметры, вы должны изменить null в операторе Expression.Call на new Type[] { returnType }.Если это генерируется один раз для каждого типа, который вы передаете, и вы планируете много его использовать, то было бы неплохо зашифровать эти функции в Dictionary<Type, Func<Type, object>> и собрать его только один раз (поскольку многократная компиляция выражений - пустая тратасистемные ресурсы).

...