使用反射查找枚举器的方法
Using reflection to find an enumerator's method
背景:
我正在使用 Harmony Library 修改现有代码。现有的 C# 代码遵循以下结构:
public class ToModify
{
public override void Update()
{
foreach (StatusItemGroup.Entry entry in collection)
{
// I am trying to alter an operation at the end of this loop.
}
}
}
public class StatusItemGroup
{
public IEnumerator<Entry> GetEnumerator()
{
return items.GetEnumerator();
}
private List<Entry> items = new List<Entry>();
public struct Entry { }
}
鉴于这种情况,我必须修改正在生成的 IL 代码,为此我必须获得目标操作数的 MethodInfo
。这是目标:
IL_12B6: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
问题:
如何为枚举器的 MoveNext
方法获取 MethodInfo
?
我试过的:
我能想到的一切都产生了 null
结果。这是我最基本的尝试:
MethodInfo targetMethod = typeof(IEnumerator<StatusItemGroup.Entry>).GetMethod("MoveNext");
我不明白为什么这不起作用,我不知道我需要做什么才能正确获取 MethodInfo
。
MoveNext
不是在 IEnumerator<T>
上定义的,而是在 IEnumerator<T>
继承的非泛型 IEnumerator
上定义的。
接口继承结合反射有点奇怪,所以你需要直接从定义它的基接口获取方法信息:
MethodInfo targetMethod = typeof(System.Collections.IEnumerator).GetMethod("MoveNext");
使用免费的 LinqPad,我用 Harmony 2.0 RC2 创建了这个。如您所见,我是否使用传递后缀来更改枚举器并将其包装起来。还有其他方法,我怀疑您实际上在某处有一个 IEnumeration。通过直接在 returns IEnumeration 的原始方法上使用直通后缀,这将更容易修补。在那种情况下不需要包装枚举器。
但我不知道您的完整用例,所以现在,这是工作示例:
void Main()
{
var harmony = new Harmony("test");
harmony.PatchAll();
var group = new StatusItemGroup();
var items = new List<StatusItemGroup.Entry>() { StatusItemGroup.Entry.Make("A"), StatusItemGroup.Entry.Make("B") };
Traverse.Create(group).Field("items").SetValue(items);
var enumerator = group.GetEnumerator();
while(enumerator.MoveNext())
Console.WriteLine(enumerator.Current.id);
}
[HarmonyPatch]
class Patch
{
public class ProxyEnumerator<T> : IEnumerable<T>
{
public IEnumerator<T> enumerator;
public Func<T, T> transformer;
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public IEnumerator<T> GetEnumerator()
{
while(enumerator.MoveNext())
yield return transformer(enumerator.Current);
}
}
[HarmonyPatch(typeof(StatusItemGroup), "GetEnumerator")]
static IEnumerator<StatusItemGroup.Entry> Postfix(IEnumerator<StatusItemGroup.Entry> enumerator)
{
StatusItemGroup.Entry Transform(StatusItemGroup.Entry entry)
{
entry.id += "+";
return entry;
}
var myEnumerator = new ProxyEnumerator<StatusItemGroup.Entry>()
{
enumerator = enumerator,
transformer = Transform
};
return myEnumerator.GetEnumerator();
}
}
public class StatusItemGroup
{
public IEnumerator<Entry> GetEnumerator()
{
return items.GetEnumerator();
}
private List<Entry> items = new List<Entry>();
public struct Entry
{
public string id;
public static Entry Make(string id) { return new Entry() { id = id }; }
}
}
背景:
我正在使用 Harmony Library 修改现有代码。现有的 C# 代码遵循以下结构:
public class ToModify
{
public override void Update()
{
foreach (StatusItemGroup.Entry entry in collection)
{
// I am trying to alter an operation at the end of this loop.
}
}
}
public class StatusItemGroup
{
public IEnumerator<Entry> GetEnumerator()
{
return items.GetEnumerator();
}
private List<Entry> items = new List<Entry>();
public struct Entry { }
}
鉴于这种情况,我必须修改正在生成的 IL 代码,为此我必须获得目标操作数的 MethodInfo
。这是目标:
IL_12B6: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
问题:
如何为枚举器的 MoveNext
方法获取 MethodInfo
?
我试过的:
我能想到的一切都产生了 null
结果。这是我最基本的尝试:
MethodInfo targetMethod = typeof(IEnumerator<StatusItemGroup.Entry>).GetMethod("MoveNext");
我不明白为什么这不起作用,我不知道我需要做什么才能正确获取 MethodInfo
。
MoveNext
不是在 IEnumerator<T>
上定义的,而是在 IEnumerator<T>
继承的非泛型 IEnumerator
上定义的。
接口继承结合反射有点奇怪,所以你需要直接从定义它的基接口获取方法信息:
MethodInfo targetMethod = typeof(System.Collections.IEnumerator).GetMethod("MoveNext");
使用免费的 LinqPad,我用 Harmony 2.0 RC2 创建了这个。如您所见,我是否使用传递后缀来更改枚举器并将其包装起来。还有其他方法,我怀疑您实际上在某处有一个 IEnumeration。通过直接在 returns IEnumeration 的原始方法上使用直通后缀,这将更容易修补。在那种情况下不需要包装枚举器。
但我不知道您的完整用例,所以现在,这是工作示例:
void Main()
{
var harmony = new Harmony("test");
harmony.PatchAll();
var group = new StatusItemGroup();
var items = new List<StatusItemGroup.Entry>() { StatusItemGroup.Entry.Make("A"), StatusItemGroup.Entry.Make("B") };
Traverse.Create(group).Field("items").SetValue(items);
var enumerator = group.GetEnumerator();
while(enumerator.MoveNext())
Console.WriteLine(enumerator.Current.id);
}
[HarmonyPatch]
class Patch
{
public class ProxyEnumerator<T> : IEnumerable<T>
{
public IEnumerator<T> enumerator;
public Func<T, T> transformer;
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public IEnumerator<T> GetEnumerator()
{
while(enumerator.MoveNext())
yield return transformer(enumerator.Current);
}
}
[HarmonyPatch(typeof(StatusItemGroup), "GetEnumerator")]
static IEnumerator<StatusItemGroup.Entry> Postfix(IEnumerator<StatusItemGroup.Entry> enumerator)
{
StatusItemGroup.Entry Transform(StatusItemGroup.Entry entry)
{
entry.id += "+";
return entry;
}
var myEnumerator = new ProxyEnumerator<StatusItemGroup.Entry>()
{
enumerator = enumerator,
transformer = Transform
};
return myEnumerator.GetEnumerator();
}
}
public class StatusItemGroup
{
public IEnumerator<Entry> GetEnumerator()
{
return items.GetEnumerator();
}
private List<Entry> items = new List<Entry>();
public struct Entry
{
public string id;
public static Entry Make(string id) { return new Entry() { id = id }; }
}
}