生成 enumerator.Current 而不是 (int)((List<T>.Enumerator*)(byte*)enumerator)->Current

Generating enumerator.Current instead of (int)((List<T>.Enumerator*)(byte*)enumerator)->Current

我目前正在研究 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,而不是像现在那样乱七八糟?

List<T>.Enumerator 是值类型。因此,您需要在 enumerator 变量的 地址 上调用方法,而不是在其值上调用方法。

您也不能对值类型使用 callvirt(尽管您可以进行约束虚拟调用,这对于从 object 调用某些方法很有用)。你需要在这里使用 call。这不是问题,因为值类型不能被子类化,所以你知道你正在调用的确切方法。

因此您需要:

ilProcessor.Emit(OpCodes.Ldloca_S, enumeratorVariable);
ilProcessor.Emit(OpCodes.Call, getCurrentMethodReference);

这解释了为什么你得到奇怪的反编译输出:反编译器知道 Current 只能在 enumerator 的地址上调用,但它也看到你实际上是在调用它在值上,因此它编造了转换以将 enumerator 变成指向 List<T>.Enumerator.

的指针

你可以看到 on SharpLab.