我目前正在开发一个API。
每当用户用某个属性标记一个类型时,我都希望创建一个新的List<int>字段并循环它,执行一些操作。
以下是一些相关代码:
TypeReference intTypeReference = moduleDefinition.ImportReference(typeof(int));
TypeReference listType = moduleDefinition.ImportReference(typeof(List<>));
GenericInstanceType intListType = listType.MakeGenericInstanceType(intTypeReference);
var numberList =
new FieldDefinition(
name: "Numbers",
attributes: field.Attributes,
fieldType: moduleDefinition.ImportReference(intListType));
generatedType.Fields.Add(numberList);
Type enumeratorType = typeof(List<>.Enumerator);
var enumeratorTypeReference = moduleDefinition.ImportReference(enumeratorType);
GenericInstanceType intEnumeratorType = enumeratorTypeReference.MakeGenericInstanceType(intTypeReference);
var enumeratorVariable = new VariableDefinition(intEnumeratorType);
convertMethod.Body.Variables.Add(enumeratorVariable);
ilProcessor.Emit(OpCodes.Ldarg_0); // this
ilProcessor.Emit(OpCodes.Ldfld, numberList);
MethodReference getEnumeratorMethodReference =
new MethodReference(
name: "GetEnumerator",
returnType: intEnumeratorType,
declaringType: intListType)
{
HasThis = true
};
ilProcessor.Emit(OpCodes.Callvirt, getEnumeratorMethodReference);
ilProcessor.Emit(OpCodes.Stloc, enumeratorVariable);
TypeDefinition enumeratorTypeDefinition = enumeratorTypeReference.Resolve();
MethodDefinition getCurrentMethod =
enumeratorTypeDefinition.Properties.Single(p => p.Name == "Current").GetMethod;
MethodDefinition moveNextMethod =
enumeratorTypeDefinition.Methods.Single(m => m.Name == "MoveNext");
MethodReference getCurrentMethodReference = moduleDefinition.ImportReference(getCurrentMethod);
MethodReference moveNextMethodReference = moduleDefinition.ImportReference(moveNextMethod);
// Call enumerator.Current
ilProcessor.Emit(OpCodes.Ldloc, enumeratorVariable);
ilProcessor.Emit(OpCodes.Callvirt, getCurrentMethodReference);
// Store it inside currentVariable
ilProcessor.Emit(OpCodes.Stloc, currentVariable);
ilProcessor.Emit(OpCodes.Nop);以下是相关的输出:
List<int>.Enumerator enumerator = Numbers.GetEnumerator();
int value = (int)((List<T>.Enumerator*)(byte*)enumerator)->Current;List<int>.Enumerator enumerator = Numbers.GetEnumerator();是我想要的结果。然而,int value = (int)((List<T>.Enumerator*)(byte*)enumerator)->Current;显然不是我想要的。
我应该怎么做才能使输出变成int value = enumerator.Current,而不是目前那种不可读的混乱呢?
发布于 2020-01-10 09:30:37
List<T>.Enumerator是一种值类型。因此,您需要对enumerator变量的地址而不是它的值调用方法。
您也不能对值类型使用callvirt (尽管您可以执行受限的虚拟调用,这对于从object调用某些方法很有用)。您需要在这里使用call。这不是一个问题,因为值类型不能被子类化,所以您知道所调用的确切方法。
因此,你需要:
ilProcessor.Emit(OpCodes.Ldloca_S, enumeratorVariable);
ilProcessor.Emit(OpCodes.Call, getCurrentMethodReference);这就解释了为什么要得到奇怪的反编译输出:反编译器知道只能在enumerator的地址上调用enumerator,但它也看到实际上是对值调用它,所以它将转换为将enumerator转换为指向List<T>.Enumerator的指针。
你可以看到那个论SharpLab。
https://stackoverflow.com/questions/59678485
复制相似问题