#c# #reflection #fieldinfo
#c# #отражение #fieldinfo
Вопрос:
В C # я определяю статическое поле определенного класса. Изнутри класса я хочу иметь возможность отображать имя статического поля, примерно так:
public class Unit {
public string NameOfField { get { return ...; } }
}
public static Unit Hectare = new Unit();
Если я сейчас получу доступ:
Hectare.NameOfField
Я хочу, чтобы оно возвращало:
Hectare
Я знаю, что существует система статических функций.Отражение.База методов.GetCurrentMethod(), но, насколько я могу судить, нет способа получить имя экземпляра, содержащего этот текущий метод?
Существует также структура System.RuntimeFieldHandle, но я не смог идентифицировать какой-либо метод GetCurrentFieldHandle() .
Я не уверен, что упускаю что-то очевидное?
Любая помощь по этому вопросу очень ценится.
Комментарии:
1. Не может быть способа сделать это изнутри класса Unit. Что, если на один и тот же экземпляр ссылается более одной переменной?
2. Разве вы не можете просто передать имя конструктору и сохранить его в поле в Unit?
public static Unit Hectare = new Unit("Hectare");
3. Кажется, вы запрашиваете имя переменной среды выполнения, которой назначен экземпляр вашего класса. Вы не можете сделать это с помощью c#
4. Спасибо за ваши комментарии, Blorgbeard, Хенрик и Ян. Все это имеет смысл 🙂 Возможно, мне придется сделать это так, как вы, Хенрик (а также @Gerrie и @Manji), предлагаете. Я надеялся избежать дополнительного ввода, но такова жизнь…
Ответ №1:
Вам не следует рассчитывать на имена переменных в ваших разработках, поскольку они не завершаются во время выполнения.
Лучше инициализировать Unit именем напрямую:
public class Unit {
public Unit(string name)
{
NameOfField = name;
}
public string NameOfField { get; private set;} }
}
public static Unit Hectare = new Unit("Hectare");
Комментарии:
1. Спасибо за ответ, Манджи. Я думал об этом, но у меня довольно много статических полей, и я хочу избежать требуемого рефакторинга. Но мне, возможно, придется пересмотреть…
2. да, так лучше. Может быть, вы можете использовать регулярное выражение для выполнения масштабного поиска / замены с помощью vsiual studio: найдите
public[ ] static[ ] Unit[ ] {[^ ] }[ ] =[ ] new[ ] Unit()
и замените его наpublic static Unit 1 = new Unit("1")
3. Здорово, что вы предоставляете регулярное выражение, обычно у меня уходит большая часть дня, когда я пытаюсь самостоятельно составлять регулярные выражения 🙂 Как вы можете видеть ниже, я реализовал решение моей конкретной проблемы, поэтому, к счастью, мне не нужно проводить рефакторинг. В любом случае спасибо за ваши усилия.
Ответ №2:
Единственным способом обойти это будет сохранение этой информации в классе:
public static Unit Hectare = new Unit("Hectare");
При компиляции вашего кода все имена переменных теряются и заменяются внутренними ссылками. Нет способа получить это имя снова.
Комментарии:
1. Спасибо за ответ, Джерри. Как я также упоминал в ответе Манджи выше, у меня довольно много статических полей, и я хочу избежать требуемого рефакторинга. Но если другого выхода нет…
Ответ №3:
Вы можете использовать отражение для получения полей и свойств класса. Как показано ниже:
Предположим, у вас есть класс с одним свойством:
class Test
{
public static string MySupperField
{
get
{
return "Some symbols here";
}
}
}
......
Вы можете прочитать имя свойства таким образом:
public string[] GetClassStaticNames(Type T)
{
string[] names;
System.Reflection.PropertyInfo[] props = T.GetProperties(); // This will return only properties not fields! For fields obtaining use T.GetFields();
names = new string[props.Count()];
for (int i = 0; i < props.Count(); i )
{
names[i] = props[i].Name;
}
return names;
}
Надеюсь, это поможет.
[ПРАВИТЬ]
Возвращаясь к вашему вопросу — Нет, вы не можете получить имя текущей переменной.
То, о чем вы спрашиваете, не может быть выполнено из-за природы классов, они являются объектами в памяти, и ссылка на один объект может храниться во многих переменных, и когда вы запрашиваете значение поля экземпляра или свойства, фактически выполняется операция с объектом в памяти, а не с переменной, которая содержит ссылку на этот объект. Таким образом, получение имени переменной, которая содержит ссылку на текущий экземпляр, не имеет значения
Комментарии:
1. Я думал о получении имен полей из содержащего их класса. В принципе, я должен быть в состоянии сопоставить текущее поле со списком полей в содержащем классе. Я надеялся на более быстрый способ, но все ответы, которые я получил, довольно обескураживающие в этом отношении 🙂 Любым способом, большое спасибо за ответ, Антон.
2. Еще раз спасибо, Антон. @Blorgbeard сделал аналогичный комментарий выше. Я вижу смысл, тот факт, что объект может храниться во многих переменных, более или менее дисквалифицирует мой запрос. Мне придется пересмотреть свою реализацию. В любом случае, хорошо, что это обсуждалось. Спасибо.
Ответ №4:
Спасибо всем, кто нашел время ответить и обсудить мой вопрос.
Просто чтобы вы знали, я внедрил решение, которого достаточно для моих нужд. Решение не является общим, и в нем есть некоторые подводные камни, но я подумал, что в любом случае поделюсь им на случай, если оно может помочь кому-то еще.
В принципе, так выглядит класс, который используется при определении полей:
public class Unit : IUnit {
public NameOfField { get; set; }
...
}
Как вы можете видеть, класс реализует интерфейс IUnit, и я предоставил общедоступный параметр в свойстве NameOfField.
Статические поля обычно определяются подобным образом в некотором содержащем их классе:
public static Unit Hectare = new Unit();
Мое решение состоит в том, чтобы установить свойство NameOfField через отражение, прежде чем поле будет использовано в реализации.
Я делаю это через статический конструктор (который, конечно, должен быть вызван перед обращением к единичным полям.
Я использую Linq для обхода исполняемой сборки в поисках соответствующих полей, и когда я обнаружил эти поля (поля, тип которых реализует интерфейс IUnit), я устанавливаю свойство NameOfField для каждого из них, используя метод Any extension:
Assembly.GetExecutingAssembly().GetTypes().
SelectMany(type => type.GetFields(BindingFlags.Public | BindingFlags.Static)).
Where(fieldInfo => fieldInfo.FieldType.GetInterfaces().Contains(typeof(IUnit))).
Any(fieldInfo =>
{
((IUnit)fieldInfo.GetValue(null)).NameOfField= fieldInfo.Name;
return false;
});
У этого подхода есть некоторые недостатки:
- Статический конструктор должен быть вызван с помощью ручного вмешательства, прежде чем можно будет получить доступ к каким-либо единичным полям
- Параметр NameOfField является общедоступным. В моем случае это не проблема, но это может возникнуть при применении в других сценариях. (Я предполагаю, что установщик можно было бы сделать закрытым и вызвать с помощью дальнейшего отражения, но я не потратил время на дальнейшее изучение этого пути.)
- … ?
В любом случае, возможно, это решение может помочь кому-то еще, кроме меня.