Получить определение члена из ссылки в Mono.Cecil

Я заметил, что ссылки на поля или методы в определенных типах методов (например, метод в универсальном типе) будут иметь тип FieldReference, а не FieldDefinition, хотя поле (или метод) находится в том же модуле, в том же типе. Как я могу получить FieldDefinition из этого FieldReference?

Я пробовал module.Import и module.MetadataResolver.Resolve, но оба не работают.

Ответ на этот вопрос, но более общий.

ИЗМЕНИТЬ:

Простой универсальный класс:

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<> из модуля, который загружается во время выполнения AssemblyDefinition.ReadAssembly, когда .Resolve() возвращает значение null вместо возврата FieldDefinition.

ОБНОВЛЕНИЕ:

Оказалось, что из-за того, что я меняю имя поля в универсальном типе, ссылка ломается и Resolve() возвращает null. Все еще ищите достойное решение для этого.


person mrahhal    schedule 05.01.2015    source источник
comment
Вызов Resolve() для ссылки не работает? (т.е. var myFieldDefinition = myFieldReference.Resolve();)   -  person rileywhite    schedule 06.01.2015
comment
@rileywhite Нет, возвращает ноль. Сначала я думал, что это должно быть определение, но я думаю, что ссылки на члены универсальных типов обрабатываются в Cecil по-другому.   -  person mrahhal    schedule 06.01.2015
comment
Не могли бы вы опубликовать код, который воспроизводит то, что вы видите?   -  person rileywhite    schedule 07.01.2015
comment
@rileywhite ознакомьтесь с правкой, пожалуйста.   -  person mrahhal    schedule 07.01.2015


Ответы (1)


Обновлять:

Я изменил код, чтобы разделить типы на разные проекты в разных решениях. К сожалению, мне все еще не удается воспроизвести проблему, но я готов продолжать попытки, если это поможет.

Я могу разрешить ссылку на тип с помощью следующего кода. Если вы можете предоставить более подробную информацию о воспроизведении, я был бы готов попробовать еще раз :-)

Это единственный тип в сборке с именем TargetLibrary.dll. Я скомпилировал его в собственном решении и скопировал сборку в C:\Temp.

public class HelperClass<T>
{
    private int _someInt;

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

Этот код находится в другой сборке, которая скомпилирована в исполняемый файл консоли в своем собственном решении.

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>
person rileywhite    schedule 08.01.2015
comment
Спасибо! Проблема в том, что ссылка на самом деле из загруженного модуля, я говорю о модуле, который я загрузил во время выполнения. Вот когда Resolve() возвращает null, ваш пример работает с универсальным типом из исполняемой сборки, и я понятия не имею, почему это работает, а другое - нет. - person mrahhal; 08.01.2015
comment
Хммм... кажется, что у распознавателя сборки, связанного со ссылкой, могут быть проблемы с поиском сборки, что странно, потому что вы уже использовали распознаватель для загрузки сборки. Я попробую еще раз, когда у меня будет несколько минут. - person rileywhite; 08.01.2015
comment
Мои мысли точно. Чтобы было понятнее, я использую AssemblyDefinition.ReadAssembly для загрузки MainModule и продолжаю оттуда. Спасибо за помощь. - person mrahhal; 08.01.2015
comment
@mrahhal, я обновил код на основе ваших отзывов. Есть ли что-нибудь еще, о чем вы могли бы подумать, что вы могли бы делать по-другому? Я использую Mono.Cecil версии 0.9.5.4, установленную из NuGet в Visual Studio 2013 и .NET 4.5, если это имеет значение. - person rileywhite; 11.01.2015
comment
Спасибо за помощь. Я нашел проблему в своем другом вопросе здесь Я заявляю, что на самом деле меняю имена некоторых полей по ходу работы. Я обнаружил, что когда я это делаю, метод .Resolve возвращает значение null. Я принял ваш ответ, но есть ли у вас предложение о том, как это сделать, не нарушая эту ссылку? Конечно, я могу придумать решение, но я хотел бы найти простое решение, прежде чем идти другим путем. - person mrahhal; 11.01.2015