生成 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.
我目前正在研究 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.