Как вы получаете имя переменной, поскольку оно было физически напечатано в его объявлении?
Возможный дубликат:
Поиск имени переменной, переданного функции в С#
В приведенном ниже классе содержится полевое поле.
Мне нужно динамически определять имя поля, поскольку оно вводится в объявление класса
т.е. мне нужно получить строку "город" из экземпляра объекта города.
Я попытался сделать это, изучив его тип в 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