C# Вызов и возврат объекта из статического метода в IL

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

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


person OnResolve    schedule 08.03.2012    source источник


Ответы (2)


По крайней мере, одна проблема заключается в том, что вы помещаете ссылку "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. Вызвать getTypeFromHandle, который извлекает дескриптор типа из стека и помещает фактическое System.Type в стек.

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

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

person Kirk Woll    schedule 08.03.2012
comment
Таким образом, удаление load arg 0 emit позволяет вызвать статический метод, однако я хочу передать переменную returnType статическому методу, но, похоже, он передает «это». Когда я отлаживаю статический метод, параметр типа «Тип» не является значением returnType, а скорее типом динамического класса, который я создал. - person OnResolve; 09.03.2012
comment
Убедитесь, что подпись вашего метода выглядит так: object GetStoredObject(Type returnType);. Если это так, то вам действительно нужно передать аргумент. Однако что Type вы на самом деле пытаетесь передать? - person Kirk Woll; 09.03.2012
comment
общедоступный статический объект GetStoredObject (контракт типа) Мой метод, названный выше EmitReferenceMethodBody (тип returnType), получает тип, который я хочу передать моему статическому методу. - person OnResolve; 09.03.2012
comment
Работает! Большое спасибо. Ваше объяснение было очень интуитивным! - person OnResolve; 09.03.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>> и создавать их только один раз (поскольку повторная компиляция выражений - это пустая трата системных ресурсов). ).

person SPFiredrake    schedule 08.03.2012