Справедливое предупреждение, это может быть немного эзотерическим и сложным.
Учитывая членство в MemberRef (более подробное объяснение ниже), извлеченное из потока CIL, как вы определяете, какое поле, если оно есть, указывает (и получает FieldInfo
для него)?
Вот что я понял до сих пор
В соответствии с стандартом ECMA 335 MemberRef является токеном метаданных, который представляет собой в основном поиск в таблице, который может указывать либо на поле токен метаданных или токен метаданных метода. Любое начало токена метаданных 0x0A является MemberRef.
Раньше я не встречал ни одного из них, но они не кажутся такими необычными. Я смог получить один из них, используя следующий анонимный тип в методе:
new
{
A = new DateTime(1234, 5, 6, 7, 8, 9, DateTimeKind.Utc),
B = (DateTime?)null
}
Когда я захватываю тело метода через отражение (получаем PropertyInfo, получаем GetMethod, получите MethodBody, а затем получить IL) A
get метод:
[2, 123, 79, 0, 0, 10, 42]
Что преобразуется в:
ldarg.0
ldfld 0x0A00004F
ret
Если я задумываюсь и получаю фоновое поле (полагаясь на подобие имени на выбор <A>i__Field
, ничего не алгоритмическое), я вижу, что MetadataToken is 0x04000056
.
Обратите внимание, что генерируемые токены могут различаться между компиляциями.
Маркер, начинающийся с 0x04, является полем:
В большинстве случаев (для всех не анонимных объектов в моем ограниченном тестировании) IL содержит маркер метаданных поля. Это легко превратить в FieldInfo
через Module.ResolveField(int)
, выясняя, что делать с членом MemberRef, отключает меня.
Прокручивая другие методы ResolveXXX на Module, единственное, что может сделать что-либо с MemberRef, это ResolveSignature
. При запуске на вышеприведенном MemberRef он возвращает массив [6, 19, 0]
. Я не знаю, что с этим делать.
Код, над которым я работаю, не закончен, но открыт. Ошибка можно увидеть, выполнив этот тест, в результате чего возникает исключение при сбое поиска в поле эта строка. Обратите внимание, что сам тест не завершен, он не ожидается, что он достигнет успеха, но он также не должен умереть.
Кто-нибудь знает, что делать с этой сигнатурой или каким-либо другим способом получить к токену метаданных поля (и, следовательно, его FieldInfo
) из MemberRef?
Здесь программа LINQPad script, которая воспроизводит проблему. Он довольно большой, там много шаблонов.
void Main()
{
Init();
var obj =
new
{
A = new DateTime(1234, 5, 6, 7, 8, 9, DateTimeKind.Utc),
B = (DateTime?)null
};
var usage = PropertyFieldUsage(obj.GetType());
usage.Dump();
}
private static Dictionary<int, System.Reflection.Emit.OpCode> OneByteOps;
private static Dictionary<int, System.Reflection.Emit.OpCode> TwoByteOps;
public static Dictionary<PropertyInfo, List<FieldInfo>> PropertyFieldUsage(Type t)
{
var ret = new Dictionary<PropertyInfo, List<FieldInfo>>();
var props = t.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic).Where(p => p.GetMethod != null);
var module = t.Module;
foreach (var prop in props)
{
var getMtd = prop.GetMethod;
var mtdBody = getMtd.GetMethodBody();
var il = mtdBody.GetILAsByteArray();
var fieldHandles = _GetFieldHandles(il);
var fieldInfos =
fieldHandles
.Select(
f => module.ResolveField(f)
).ToList();
ret[prop] = fieldInfos;
}
return ret;
}
// Define other methods and classes here
private static List<int> _GetFieldHandles(byte[] cil)
{
var ret = new List<int>();
int i = 0;
while (i < cil.Length)
{
int? fieldHandle;
System.Reflection.Emit.OpCode ignored;
var startsAt = i;
i += _ReadOp(cil, i, out fieldHandle, out ignored);
if (fieldHandle.HasValue)
{
ret.Add(fieldHandle.Value);
}
}
return ret;
}
private static int _ReadOp(byte[] cil, int ix, out int? fieldHandle, out System.Reflection.Emit.OpCode opcode)
{
const byte ContinueOpcode = 0xFE;
int advance = 0;
byte first = cil[ix];
if (first == ContinueOpcode)
{
var next = cil[ix + 1];
opcode = TwoByteOps[next];
advance += 2;
}
else
{
opcode = OneByteOps[first];
advance++;
}
fieldHandle = _ReadFieldOperands(opcode, cil, ix, ix + advance, ref advance);
return advance;
}
private static int? _ReadFieldOperands(System.Reflection.Emit.OpCode op, byte[] cil, int instrStart, int operandStart, ref int advance)
{
Func<int, int> readInt = (at) => cil[at] | (cil[at + 1] << 8) | (cil[at + 2] << 16) | (cil[at + 3] << 24);
switch (op.OperandType)
{
case System.Reflection.Emit.OperandType.InlineBrTarget:
advance += 4;
return null;
case System.Reflection.Emit.OperandType.InlineSwitch:
advance += 4;
var len = readInt(operandStart);
var offset1 = instrStart + len * 4;
for (var i = 0; i < len; i++)
{
advance += 4;
}
return null;
case System.Reflection.Emit.OperandType.ShortInlineBrTarget:
advance += 1;
return null;
case System.Reflection.Emit.OperandType.InlineField:
advance += 4;
var field = readInt(operandStart);
return field;
case System.Reflection.Emit.OperandType.InlineTok:
case System.Reflection.Emit.OperandType.InlineType:
case System.Reflection.Emit.OperandType.InlineMethod:
advance += 4;
return null;
case System.Reflection.Emit.OperandType.InlineI:
advance += 4;
return null;
case System.Reflection.Emit.OperandType.InlineI8:
advance += 8;
return null;
case System.Reflection.Emit.OperandType.InlineNone:
return null;
case System.Reflection.Emit.OperandType.InlineR:
advance += 8;
return null;
case System.Reflection.Emit.OperandType.InlineSig:
advance += 4;
return null;
case System.Reflection.Emit.OperandType.InlineString:
advance += 4;
return null;
case System.Reflection.Emit.OperandType.InlineVar:
advance += 2;
return null;
case System.Reflection.Emit.OperandType.ShortInlineI:
advance += 1;
return null;
case System.Reflection.Emit.OperandType.ShortInlineR:
advance += 4;
return null;
case System.Reflection.Emit.OperandType.ShortInlineVar:
advance += 1;
return null;
default: throw new Exception("Unexpected operand type [" + op.OperandType + "]");
}
}
static void Init()
{
var oneByte = new List<System.Reflection.Emit.OpCode>();
var twoByte = new List<System.Reflection.Emit.OpCode>();
foreach (var field in typeof(System.Reflection.Emit.OpCodes).GetFields(BindingFlags.Public | BindingFlags.Static))
{
var op = (System.Reflection.Emit.OpCode)field.GetValue(null);
if (op.Size == 1)
{
oneByte.Add(op);
continue;
}
if (op.Size == 2)
{
twoByte.Add(op);
continue;
}
throw new Exception("Unexpected op size for " + op);
}
OneByteOps = oneByte.ToDictionary(d => (int)d.Value, d => d);
TwoByteOps = twoByte.ToDictionary(d => (int)(d.Value & 0xFF), d => d);
}