【问题标题】:Get member definition from reference in Mono.Cecil从 Mono.Cecil 中的引用中获取成员定义
【发布时间】:2015-01-11 13:35:31
【问题描述】:

我注意到某些类型的方法(例如泛型类型中的方法)中的字段或方法引用将是FieldReference 类型,而不是FieldDefinition,尽管字段(或方法)是相同的模块,同类型。我怎样才能从这个FieldReference 获得FieldDefinition

我尝试了module.Importmodule.MetadataResolver.Resolve,但都不起作用。

this 问题的跟进,但更一般。

编辑:

一个简单的泛型类:

public class HelperClass<T>
{
    private int _someInt;

    void SomeMethod(int i)
    {
        _someInt = i;
    }
}

SomeMethod 的正文包含:

...
IL_0008: ldarg.0
IL_0009: ldarg.1
IL_000a: stfdl System.Int32 HelperClass`1<T>::_someInt
....

IL_000a 操作码的操作数通常应该是FieldDefinition,毕竟它在同一个模块中。但是因为我想HelperClass 是通用的,所以操作数是无法解析的FieldReference,我只能希望比较全名以实际找到FieldDefinition

在这种情况下,这不是什么大问题,但是当引用是对其他泛型类型中的其他成员时,我确信有比枚举所有类型以找到定义更好的方法.

编辑:

HelperClass&lt;&gt; 来自AssemblyDefinition.ReadAssembly 在运行时加载的模块,此时.Resolve() 返回null 而不是返回FieldDefinition

更新:

事实证明,因为我正在更改泛型类型中的字段名称,所以引用中断,Resolve() 返回 null。仍在为this one 寻找合适的解决方案。

【问题讨论】:

  • 在引用上调用 Resolve() 不起作用吗? (即var myFieldDefinition = myFieldReference.Resolve();
  • @rileywhite 不,返回空值。一开始我认为它应该是一个定义,但我认为对泛型类型成员的引用在 Cecil 中的处理方式不同。
  • 您能发布重现您所见内容的代码吗?
  • @rileywhite 请查看编辑。

标签: c# .net mono.cecil


【解决方案1】:

更新:

我已更改代码以在不同的解决方案中将类型分成不同的项目。不幸的是,我仍然无法重现该问题,但如果有帮助,我愿意继续尝试。

我可以使用以下代码解析类型引用。如果您能够提供更多详细信息以进行复制,我愿意再尝试一下:-)

这是名为 TargetLibrary.dll 的程序集中的唯一类型。我在它自己的解决方案中编译它,并将程序集复制到C:\Temp

public class HelperClass<T>
{
    private int _someInt;

    void SomeMethod(int i)
    {
        _someInt = i;
    }
}

此代码位于不同的程序集中,该程序集在其自己的解决方案中编译成控制台 exe 文件。

class Program
{
    static void Main(string[] args)
    {
        var module = AssemblyDefinition.ReadAssembly(@"C:\Temp\TargetLibrary.dll").MainModule;

        Console.WriteLine("For HelperClass<>");
        var helperClass = module.Types[1];
        var someMethod = helperClass.Methods[0];
        var someMethodBody = someMethod.Body;
        foreach (var instruction in someMethodBody.Instructions)
        {
            Console.WriteLine(
                "{0}\t{1}\t{2}",
                instruction.Offset,
                instruction.OpCode.Code,
                instruction.Operand == null ? "<null>" : string.Format("{0} / {1}", instruction.Operand.GetType().FullName, instruction.Operand.ToString()));

            var fieldReference = instruction.Operand as FieldReference;
            if (fieldReference != null)
            {
                var fieldDefinition = fieldReference.Resolve();
                Console.WriteLine(
                    "\t\tResolved field reference operand: {0} / {1}",
                    fieldDefinition.GetType().FullName,
                    fieldDefinition.ToString());
            }
        }
    }
}

运行它会产生以下输出。

For HelperClass<>
0       Ldarg_0 <null>
1       Ldarg_1 <null>
2       Stfld   Mono.Cecil.FieldReference / System.Int32 TargetLibrary.HelperClass`1<T>::_someInt
                Resolved field reference operand: Mono.Cecil.FieldDefinition / System.Int32 TargetLibrary.HelperClass`1::_someInt
7       Ret     <null>

【讨论】:

  • 谢谢!问题是引用实际上来自加载的模块,我说的是我在运行时加载的模块。那是当 Resolve() 返回 null 时,您的示例正在使用正在执行的程序集中的泛型类型,我不知道为什么这有效而另一个无效。
  • 嗯...与引用关联的程序集解析器似乎在定位程序集时遇到问题,这很奇怪,因为您已经使用解析器加载程序集。等我有几分钟的时间,我会再试一次。
  • 我的想法完全正确。为了让事情更清楚,我使用AssemblyDefinition.ReadAssembly 来加载 MainModule,然后从那里开始。感谢您的帮助。
  • @mrahhal,我已根据您的反馈更新了代码。还有什么你能想到的你可能会做不同的事情吗?我正在使用从 Visual Studio 2013 和 .NET 4.5 中的 NuGet 安装的 Mono.Cecil 版本 0.9.5.4,以防万一。
  • 感谢所有帮助。我发现了问题,在我的另一个问题here 我声明我实际上正在更改一些字段的名称。我发现当我这样做时,“.Resolve”方法返回 null。我已经接受了您的回答,但是您对如何在不破坏此参考的情况下做到这一点有什么建议吗?我当然可以想出一个解决方案,但我想先找一个简单的,然后再另辟蹊径。
猜你喜欢
  • 1970-01-01
  • 2019-06-10
  • 2014-02-17
  • 2020-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多