MSDN.WhiteKnight - Stack Overflow answers
Ответ на "Как автоматически присвоить полю его же имя?"
Answer 953109
Решение для .NET 4.5+ / .NET Core
using System.Runtime.CompilerServices; public static class MyName { public static string Get([CallerMemberName] string s = "") { return s; } } public class Class1 { public static string name1 = "name1"; public static string name2 = MyName.Get(); }
Примечание. Тот факт, что при использовании в инициализаторе поля CallerMemberName возвращает имя этого поля (а не имя конструктора, который на самом деле является caller'ом), не документирован. Но это работает.
Решение для более старых версий .NET
using System; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; public static class MyName { static OpCode FindOpCode(short val) { OpCode ret = OpCodes.Nop; FieldInfo[] mas = typeof(OpCodes).GetFields(); for (int i = 0; i < mas.Length; i++) { if (mas[i].FieldType == typeof(OpCode)) { OpCode opcode = (OpCode)mas[i].GetValue(null); if (opcode.Value == val) { ret = opcode; break; } } } return ret; } static string GetFieldNameFromOffset(MethodBase mi, int offset) { MethodBody mb = null; string result = ""; //получаем тело метода mb = mi.GetMethodBody(); if (mb == null) throw new ApplicationException("Fatal error: GetMethodBody failed!"); //получаем IL-код var msil = mb.GetILAsByteArray(); //получаем модуль, в котором расположен метод var module = mi.Module; short op; int n = offset; //парсим IL-код... while (true) { if (n >= msil.Length) break; //получаем код операции if (msil[n] == 0xfe) op = (short)(msil[n + 1] | 0xfe00); else op = (short)(msil[n]); //найдем имя операции OpCode opcode = FindOpCode(op); string str = opcode.Name; int size = 0; //найдем размер операции switch (opcode.OperandType) { case OperandType.InlineBrTarget: size = 4; break; case OperandType.InlineField: size = 4; break; case OperandType.InlineMethod: size = 4; break; case OperandType.InlineSig: size = 4; break; case OperandType.InlineTok: size = 4; break; case OperandType.InlineType: size = 4; break; case OperandType.InlineI: size = 4; break; case OperandType.InlineI8: size = 8; break; case OperandType.InlineNone: size = 0; break; case OperandType.InlineR: size = 8; break; case OperandType.InlineString: size = 4; break; case OperandType.InlineSwitch: size = 4; break; case OperandType.InlineVar: size = 2; break; case OperandType.ShortInlineBrTarget: size = 1; break; case OperandType.ShortInlineI: size = 1; break; case OperandType.ShortInlineR: size = 4; break; case OperandType.ShortInlineVar: size = 1; break; default: throw new Exception("Unknown operand type."); } size += opcode.Size; int token = 0; if (n > offset && (str == "stsfld" || str == "stfld")) { //найдем токен метаданных поля token = (((msil[n + 1] | (msil[n + 2] << 8)) | (msil[n + 3] << 0x10)) | (msil[n + 4] << 0x18)); //найдем поле в модуле по токену var fi = module.ResolveField(token); result = fi.Name; return result; } n += size; //пропускаем нужное число байтов } return result; } [MethodImpl(MethodImplOptions.NoInlining)] public static string Get() { //найдем вызывающий метод var stack = new StackTrace(true); var frame = stack.GetFrame(1); var method = frame.GetMethod(); //найдем имя поля по смещению в IL var name = GetFieldNameFromOffset(method, frame.GetILOffset()); return name; } } public class Class1 { public static string name1 = "name1"; public static string name2 = MyName.Get(); }
Для инициализатора поля name2 в коде конструктора компилятор генерирует последовательность IL-инструкций call и stsfld. Метод StackFrame.GetILOffset позволяет нам получить смещение инструкции call в байтах относительно начала кода конструктора, а MethodInfo.GetMethodBody позволяет получить сам IL-код конструктора. Чтобы получить FieldInfo, достаточно найти инструкцию, следующую за call, вытащить токен метаданных поля и передать его в метод Module.ResolveField. Из FieldInfo уже легко получить имя поля.
Content is retrieved from StackExchange API.
Auto-generated by ruso-archive tools.