Как вы получаете имя переменной, поскольку оно было физически напечатано в его объявлении?

Возможный дубликат:
Поиск имени переменной, переданного функции в С#

В приведенном ниже классе содержится полевое поле.

Мне нужно динамически определять имя поля, поскольку оно вводится в объявление класса т.е. мне нужно получить строку "город" из экземпляра объекта города.

Я попытался сделать это, изучив его тип в DoSomething(), но не смог найти его при проверке содержимого Type в отладчике.

Возможно ли это?

public class Person
{
  public string city = "New York";

  public Person()
  {
  }


  public void DoSomething()
  {
    Type t = city.GetType();

    string field_name = t.SomeUnkownFunction();
    //would return the string "city" if it existed!
  }
}

Некоторые люди в своих ответах ниже спрашивают меня, почему я хочу это сделать. Вот почему.

В моей реальной ситуации, над городом есть пользовательский атрибут.

[MyCustomAttribute("param1", "param2", etc)]
public string city = "New York";

Мне нужен этот атрибут в другом коде. Чтобы получить атрибут, я использую отражение. И в коде отражения мне нужно ввести строку "город"

MyCustomAttribute attr;
Type t = typeof(Person);

foreach (FieldInfo field in t.GetFields())
{

  if (field.Name == "city")
  {
    //do stuff when we find the field that has the attribute we need
  }

}

Теперь это не безопасный тип. Если я изменил переменную "city" на "workCity" в моем объявлении поля в Person, эта строка потерпит неудачу, если я не знаю, как обновить строку

if (field.Name == "workCity")
//I have to make this change in another file for this to still work, yuk!
{
}

Итак, я пытаюсь найти способ передать строку в этот код без физического ввода.

Да, я могу объявить его как строчную константу в Person (или что-то в этом роде), но это все равно будет печатать дважды.

Уф! Это было трудно объяснить!

Спасибо

Спасибо всем, кто ответил на это * много *. Это послало меня на новый путь, чтобы лучше понять лямбда-выражения. И это создало новый вопрос.

Ответы

Ответ 1

Возможно, вам это нужно. Прекрасно работает.

Я нашел здесь здесь.

static void Main(string[] args)
{
    var domain = "matrix";
    Check(() => domain);
    Console.ReadLine();
}

static void Check<T>(Expression<Func<T>> expr)
{
    var body = ((MemberExpression)expr.Body);
    Console.WriteLine("Name is: {0}", body.Member.Name);
    Console.WriteLine("Value is: {0}", ((FieldInfo)body.Member)
   .GetValue(((ConstantExpression)body.Expression).Value));
}

Выход будет:

Name is: 'domain'
Value is: 'matrix'

Ответ 2

Я знаю, что это старый вопрос, но я пытался добиться того же, и Google отправил меня сюда. Через несколько часов я наконец нашел способ. Я надеюсь, что кто-то еще найдет это полезным.

На самом деле существует больше способов сделать это:

static void Main(string[] args) 
{
  GetName(new { var1 });
  GetName2(() => var1);
  GetName3(() => var1);
}

static string GetName<T>(T item) where T : class 
{
  return typeof(T).GetProperties()[0].Name;
}

static string GetName2<T>(Expression<Func<T>> expr) 
{
  return ((MemberExpression)expr.Body).Member.Name;
}

static string GetName3<T>(Func<T> expr) 
{
  return expr.Target.GetType().Module.ResolveField(BitConverter.ToInt32(expr.Method.GetMethodBody().GetILAsByteArray(), 2)).Name;
}

Первая - самая быстрая. Последние 2 примерно в 20 раз медленнее первого.

http://abdullin.com/journal/2008/12/13/how-to-find-out-variable-or-parameter-name-in-c.html

Ответ 3

city в этом случае является экземпляром типа string. Когда вы вызываете .GetType(), вы возвращаете фактический тип строки, который не знает вообще вашего конкретного экземпляра города.

Мне трудно понять, почему вы не можете просто набирать "город" в коде как строковый литерал здесь, если это вам нужно. Возможно, это поможет, если вы поделитесь тем, что хотите использовать это значение, и в каких обстоятельствах вы будете называть свою функцию DoSomething().

В настоящее время я полагаю, что то, что вы действительно хотите сделать, отражает весь класс Person, чтобы получить список полей в этом классе:

public void DoSomething()
{
    MemberInfo[] members = this.GetType().GetMembers();

    // now you can do whatever you want with each of the members,
    // including checking their .Name properties.
}

Хорошо, на основе вашего редактирования у меня есть еще кое-что для вас.

Вы можете найти имя полей, которые украшены вашим атрибутом во время выполнения, например:

Type t = typeof(Person);
foreach (MemberInfo member in t.GetMembers()
          .Where(m => 
                m.GetCustomAttributes(typeof(MyCustomAttribute)).Any()  ) )
{
    // "member" is a MemberInfo object for a Peson member that is 
    // decorated with your attribute
}

Вы также можете использовать флаги привязки в первом вызове GetMembers(), чтобы ограничить его только полями, если хотите.

Ответ 4

Вы упомянули "i.e. Мне нужно получить строку" город "из экземпляра объекта города". Вы хотите получить имя поля из значения поля. Например: если есть объект Person 1 с городом "Нью-Йорк", а другой с городом "Лондон", вы ищете функцию, чтобы вернуть "город". Это то, что вы подразумеваете под динамикой?


С вашим текущим дизайном вам всегда нужно сравнить имя поля из поля FieldInfo со строкой. Что, если вы вместо этого разделите это так, чтобы удерживать идентификатор для использования в целях сравнения во время отражения как части атрибута. Что-то вроде этого:

 public enum ReflectionFields
{
    CITY = 0,
    STATE,
    ZIP,    
    COUNTRY

}

[AttributeUsage(AttributeTargets.Field,AllowMultiple=false)]
public class CustomFieldAttr : Attribute
{
    public ReflectionFields Field { get; private set; }
    public string MiscInfo { get; private set; }

    public CustomFieldAttr(ReflectionFields field, string miscInfo)
    {
        Field = field;
        MiscInfo = miscInfo;
    }
}

public class Person
{
    [CustomFieldAttr(ReflectionFields.CITY, "This is the primary city")]
    public string _city = "New York";

    public Person()
    {
    }
    public Person(string city)
    {
        _city = city;
    }

}

public static class AttributeReader<T> where T:class
{
    public static void Read(T t)
    {
        //get all fields which have the "CustomFieldAttribute applied to it"
        var fields = t.GetType().GetFields().Where(f => f.GetCustomAttributes(typeof(CustomFieldAttr), true).Length == 1);

        foreach (var field in fields)
        {
            var attr = field.GetCustomAttributes(typeof(CustomFieldAttr), true).First() as CustomFieldAttr;
            if (attr.Field == ReflectionFields.CITY)
            {
                //You have the field and you know its the City,do whatever processing you need.
                Console.WriteLine(field.Name);
            }
        }            
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        PPerson p1 = new PPerson("NewYork");
        PPerson p2 = new PPerson("London");
        AttributeReader<PPerson>.Read(p1);
        AttributeReader<PPerson>.Read(p2);

}
 }

Теперь вы можете свободно переименовать поле _city Person на что-то еще, и ваш код вызова по-прежнему будет работать, поскольку код с использованием отражения пытается идентифицировать поле, используя значение перечисления ReflectionFields, установленное как часть инициализации атрибута, установленного в поле.

Ответ 5

Да его возможно!!!

Попробуйте это...

  public string DoSomething(object city)
  {
       return city.GetType().GetProperty("Name",typeof(string)).GetValue(city,null);
  }

Ответ 6

Две вещи здесь.

Номер один, как указано выше, вы получаете Type для строки, а не для Person. Таким образом, typeof (Person).GetMembers() предоставит вам список членов.

Номер два, и что более важно, похоже, что вы неправильно понимаете назначение атрибутов. В общем случае атрибуты используются для обозначения члена для конкретной обработки или для добавления дополнительной информации. Здесь вы используете имя, чтобы указать, какую обработку вы хотите, и атрибут для указания параметров, которые смешивают метафоры или что-то в этом роде.

Ответ Abhijeet более уместен, вы отмечаете поле как поле города, а затем делаете то, что вам нравится. Там, где я не согласен, я бы использовал разные классы атрибутов, а не перечисление.

Что-то вроде:

    public class MyAttribute : Attribute
    {

    }

    [AttributeUsage(AttributeTargets.Field)]
    public class MyCityAttribute : MyAttribute
    {
    }

    [AttributeUsage(AttributeTargets.Field]
    public class MyNameAttribute: MyAttribute
    {
    }

    public class Person
    {

        [MyCity]
        public string city = "New York";

        [MyCity]
        public string workCity = "Chicago";

        [MyName]
        public string fullName = "John Doe";

        public Person()
        {
        }


        public void DoSomething()
        {
            Type t = typeof(Person);
            FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public);

            foreach (var field in fields)
            {
                MyAttribute[] attributes = field.GetCustomAttributes(typeof(MyAttribute));
                if (attributes.Count > 0)
                {
                    if (attributes[0] is MyCityAttribute)
                    {
                        //Dosomething for city
                        break;
                    }

                    if (attributes[0] is MyNameAttribute)
                    {
                        //Dosomething for names
                        break;
                    }
                }
            }
        }
    }

Это позволит вам использовать разные параметры для MyCity vs MyName, которые будут иметь больше смысла в контексте обработки каждого из них.

Я думаю, что с вашим комментарием "yuk" выше, вы ударите ноготь по голове. Чтобы изменить строковую константу, если вы переименуете свою переменную, это индикатор того, что вы делаете что-то неправильно.

Ответ 7

t.GetField("city", BindingFlags.Public | BindingFlags.Instance);

или вы можете вызвать GetFields(), чтобы получить все поля

Ответ 8

Вам нужно вызвать тип get для класса Person. Итерация полей класса, как в ответе ниже

Ответ 9

Это невозможно (я думаю, что на самом деле это всего лишь несколько хаков и использование лямбда). Если вы хотите сохранить атрибуты о Person и сможете легко получить имя атрибута, я предлагаю использовать Dictionary<TKey, TValue> из пространства имен System.Collections.Generic.

И вы всегда можете создавать общедоступные свойства, которые завершают словарь.

public class Person
{
  Dictionary<string, string> attributes = new Dictionary<string, string();
  public string City
  {
    get { return attributes["city"]; }
    set { attributes["city"] = value; }
  }

  public Person()
  {
    City = "New York";
  }
}

И вы можете получить список всех атрибутов с помощью attributes.Keys.

Ответ 10

Взгляните на этот пост, поскольку он похож на то, что вы пытаетесь сделать:

Поиск имени переменной, переданного функции

(особенно ответ Конрада Рудольфа) Другой подход может состоять в том, чтобы просто добавить "город" в качестве одного из параметров в атрибуте и рыбе позже.

Ответ 11

Вы уже перебираете коллекцию объектов FieldInfo. Ищите свой атрибут на тех, и когда вы найдете FieldInfo, который содержит ваш атрибут, у вас есть тот, который вы хотите. Затем наберите .Name.

system.reflection.fieldinfo.attributes