C #: есть ли способ получить доступ к имени текущего поля?

#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 является общедоступным. В моем случае это не проблема, но это может возникнуть при применении в других сценариях. (Я предполагаю, что установщик можно было бы сделать закрытым и вызвать с помощью дальнейшего отражения, но я не потратил время на дальнейшее изучение этого пути.)
  • … ?

В любом случае, возможно, это решение может помочь кому-то еще, кроме меня.