谓词成员引用
Predicate member referencing
编译器将 LINQ 函数谓词转换为名称为“Predicate`1”的 FieldInfo。我想分析一下这些FieldInfo,看看引用了哪些成员,如果有的话。例如下面的谓词使用用户定义的方法 "ArchName":
list.FindAll(m => !m.ArchName().Contains("<>c"))
我不知道 FieldInfo 有任何相关的 MethodInfo 成员。可以做什么?
提前致谢!
这与 LINQ 无关。纯C#lambda表达式+List.
在这种情况下,Predicate
1is not a *field name*, but instead, a type, more precisely
System.Predicate`。
在我看来,您想分析方法体以查找对此类型的引用。尽管这可以通过反射实现,但使用 Mono.Cecil 会容易得多;在那种情况下,您可以使用类似的东西(请注意,这段代码在任何方面都不完整……您需要考虑错误、静态字段等):
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
class Bar
{
void Foo(List<int> items)
{
// A non sense expression :)
var r = items.FindAll(i => i % 2 == 0 || i.ToString() == "A");
}
static void Main()
{
var a = AssemblyDefinition.ReadAssembly(typeof(Bar).Assembly.Location);
var allMethods = a.MainModule.GetTypes().SelectMany(t => t.Methods);
// find instructions referencing System.Predicate<T>
var allMethodsReferencingSystemPredicate = allMethods.Where(m => m.Body.Instructions.Any(i => i.OpCode == OpCodes.Newobj && i.Operand.ToString().Contains("System.Predicate`1")));
foreach(var m in allMethodsReferencingSystemPredicate)
{
System.Console.WriteLine($"Analyzing {m}");
var instReferencingSystemPredicate = m.Body.Instructions.Where(i => i.OpCode == OpCodes.Newobj && i.Operand.ToString().Contains("System.Predicate`1"));
foreach(var inst in instReferencingSystemPredicate)
{
System.Console.WriteLine($"Checking {inst} / {inst.Previous}");
if (inst.Previous.OpCode != OpCodes.Ldftn)
{
System.Console.WriteLine($"Something went wrong. Expected LdFnt, got {inst.Previous.OpCode} in instruction {inst.Previous}");
continue;
}
// get the method being referenced.
var method = ((MethodReference) inst.Previous.Operand).Resolve();
// Analyze the method body looking for calls to `ToString()` (you will replace this with your own checks...)
foreach(var methodInst in method.Body.Instructions)
{
//System.Console.WriteLine($"Checking: {methodInst}");
if ( (methodInst.OpCode == OpCodes.Callvirt || methodInst.OpCode == OpCodes.Call) && methodInst.Operand.ToString().Contains("ToString()"))
{
System.Console.BackgroundColor = System.ConsoleColor.DarkCyan;
System.Console.WriteLine($"{method} (used in {m}) references ToString(): {methodInst}");
}
}
}
}
}
}
编译器将 LINQ 函数谓词转换为名称为“Predicate`1”的 FieldInfo。我想分析一下这些FieldInfo,看看引用了哪些成员,如果有的话。例如下面的谓词使用用户定义的方法 "ArchName":
list.FindAll(m => !m.ArchName().Contains("<>c"))
我不知道 FieldInfo 有任何相关的 MethodInfo 成员。可以做什么?
提前致谢!
这与 LINQ 无关。纯C#lambda表达式+List.
在这种情况下,Predicate
1is not a *field name*, but instead, a type, more precisely
System.Predicate`。
在我看来,您想分析方法体以查找对此类型的引用。尽管这可以通过反射实现,但使用 Mono.Cecil 会容易得多;在那种情况下,您可以使用类似的东西(请注意,这段代码在任何方面都不完整……您需要考虑错误、静态字段等):
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
class Bar
{
void Foo(List<int> items)
{
// A non sense expression :)
var r = items.FindAll(i => i % 2 == 0 || i.ToString() == "A");
}
static void Main()
{
var a = AssemblyDefinition.ReadAssembly(typeof(Bar).Assembly.Location);
var allMethods = a.MainModule.GetTypes().SelectMany(t => t.Methods);
// find instructions referencing System.Predicate<T>
var allMethodsReferencingSystemPredicate = allMethods.Where(m => m.Body.Instructions.Any(i => i.OpCode == OpCodes.Newobj && i.Operand.ToString().Contains("System.Predicate`1")));
foreach(var m in allMethodsReferencingSystemPredicate)
{
System.Console.WriteLine($"Analyzing {m}");
var instReferencingSystemPredicate = m.Body.Instructions.Where(i => i.OpCode == OpCodes.Newobj && i.Operand.ToString().Contains("System.Predicate`1"));
foreach(var inst in instReferencingSystemPredicate)
{
System.Console.WriteLine($"Checking {inst} / {inst.Previous}");
if (inst.Previous.OpCode != OpCodes.Ldftn)
{
System.Console.WriteLine($"Something went wrong. Expected LdFnt, got {inst.Previous.OpCode} in instruction {inst.Previous}");
continue;
}
// get the method being referenced.
var method = ((MethodReference) inst.Previous.Operand).Resolve();
// Analyze the method body looking for calls to `ToString()` (you will replace this with your own checks...)
foreach(var methodInst in method.Body.Instructions)
{
//System.Console.WriteLine($"Checking: {methodInst}");
if ( (methodInst.OpCode == OpCodes.Callvirt || methodInst.OpCode == OpCodes.Call) && methodInst.Operand.ToString().Contains("ToString()"))
{
System.Console.BackgroundColor = System.ConsoleColor.DarkCyan;
System.Console.WriteLine($"{method} (used in {m}) references ToString(): {methodInst}");
}
}
}
}
}
}