c# - method - ¿Cómo se obtiene el nombre de una variable tal como se escribió físicamente en su declaración?
reflection c# español (11)
Debes llamar a get type en la clase Person. La iteración de los campos de la clase como en la respuesta a continuación
Posible duplicado:
Encontrar el nombre de variable pasado a una función en C #
La clase a continuación contiene la ciudad de campo.
Necesito determinar dinámicamente el nombre del campo tal como está escrito en la declaración de la clase, es decir, necesito obtener la cadena "ciudad" de una instancia de la ciudad del objeto.
Intenté hacerlo al examinar su Tipo en DoSomething () pero no puedo encontrarlo cuando examino el contenido del Tipo en el depurador.
¿Es posible?
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!
}
}
Algunas personas en sus respuestas a continuación me han preguntado por qué quiero hacer esto. Este es el por qué.
En mi situación del mundo real, hay un atributo personalizado por encima de la ciudad.
[MyCustomAttribute("param1", "param2", etc)]
public string city = "New York";
Necesito este atributo en otro código. Para obtener el atributo, uso el reflejo. Y en el código de reflexión necesito escribir la cadena "ciudad"
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
}
}
Ahora esto no es seguro. Si cambio la variable "city" a "workCity" en mi declaración de campo en Person esta línea fallaría a menos que supiera actualizar la cadena
if (field.Name == "workCity")
//I have to make this change in another file for this to still work, yuk!
{
}
Así que estoy tratando de encontrar la forma de pasar la cadena a este código sin escribirlo físicamente.
Sí, podría declararlo como una constante de cadena en Persona (o algo por el estilo) pero aún estaría tipeando dos veces.
¡Uf! ¡Eso fue difícil de explicar!
Gracias
Gracias a todos los que respondieron esto * mucho *. Me envió a un nuevo camino para entender mejor las expresiones lambda. Y creó una nueva pregunta.
Dos cosas aquí.
Número uno, como alguien señaló anteriormente, está obteniendo el tipo de cadena, no para Persona. Entonces typeof (Person) .GetMembers () te dará la lista de miembros.
Número dos, y más importante aún, parece que está malinterpretando el propósito de los atributos. En general, los atributos se usan para marcar a un miembro para un procesamiento específico o para agregar información adicional. Aquí está usando el nombre para indicar qué procesamiento desea, y el atributo para especificar parámetros, que es la mezcla de metáforas, o algo así.
La respuesta de Abhijeet es más apropiada, marque el campo como un campo de la ciudad y luego haga lo que quiera con él. En lo que no estoy de acuerdo es en que utilizaría diferentes clases de atributos, en lugar de una enumeración.
Algo como:
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;
}
}
}
}
}
Esto le permitiría usar diferentes parámetros para MyCity vs MyName que tendrían más sentido en el contexto del procesamiento de cada uno.
Creo que con tu comentario ''yuk'' arriba, golpeas el clavo en la cabeza. Que tendrías que cambiar una constante de cadena si cambias el nombre de tu variable es un indicador de que estás haciendo algo mal.
Echa un vistazo a esta publicación, ya que se parece a lo que intentas hacer:
Encontrar el nombre de variable pasado a una función
(especialmente la respuesta de Konrad Rudolph) Otro enfoque podría ser simplemente agregar "ciudad" como uno de los parámetros en el atributo y pescar eso más tarde.
Esto no es posible (creo que en realidad lo es pero invoca varios hacks y usa lambdas). Si desea almacenar atributos sobre una Person
y poder obtener fácilmente el nombre del atributo, sugiero usar un Dictionary<TKey, TValue>
del espacio de nombres System.Collections.Generic
.
Y siempre puede hacer propiedades públicas que envuelvan el diccionario.
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";
}
}
Y puede obtener una lista de todos los atributos con attributes.Keys
. attributes.Keys
.
Quizás necesites esto. Funciona bien.
Encontré esto here .
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));
}
La salida será:
Name is: ''domain'' Value is: ''matrix''
Sé que esta es una vieja pregunta, pero estaba tratando de lograr lo mismo y google me envió aquí. Después de muchas horas finalmente encontré la manera. Espero que alguien más lo encuentre útil.
En realidad, hay más formas de lograr esto:
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;
}
El primero es el más rápido. Los últimos 2 son aproximadamente 20 veces más lentos que el primero.
http://abdullin.com/journal/2008/12/13/how-to-find-out-variable-or-parameter-name-in-c.html
Si es posible !!!
Probar esto...
public string DoSomething(object city)
{
return city.GetType().GetProperty("Name",typeof(string)).GetValue(city,null);
}
Usted mencionó "es decir, necesito obtener la cadena" ciudad "de una instancia de la ciudad objeto. ¿Está buscando obtener el nombre de campo del valor del campo? Por ejemplo: si hay 2 persona objeto uno con ciudad "Nueva York" y el otro con ciudad "Londres", ¿está buscando la función para devolver "ciudad". ¿Es esto lo que quieres decir con dinámica?
Con su diseño actual, siempre deberá comparar el nombre del campo de FieldInfo con una cadena. ¿Qué sucede si en su lugar desacoplo esto para que mantenga el identificador para utilizar con fines de comparación durante la reflexión como parte del atributo. Algo como esto:
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);
}
}
Ahora puede cambiar el nombre del campo _city de Persona a otra cosa y su código de llamada seguirá funcionando ya que el código que utiliza el reflejo intenta identificar el campo utilizando el valor enum de ReflectionFields establecido como parte de la inicialización del atributo establecido en el campo.
Ya está recorriendo la colección de objetos FieldInfo
. Busque su atributo en esos y cuando encuentre el FieldInfo
que contiene su atributo, tiene el que desea. Luego llama a .Name
en él.
city
en este caso es una instancia de tipo string
. Cuando llamas a .GetType()
, devuelves el tipo de cadena real, que no tiene ningún conocimiento de la instancia de tu ciudad en particular.
Estoy teniendo dificultades para ver por qué no puedes simplemente escribir "ciudad" en el código como una cadena literal aquí, si eso es lo que necesitas. Quizás sería útil si usted compartió para lo que quiere usar este valor y en qué circunstancias llamará a su función DoSomething()
.
En este momento, mi mejor opción es que lo que realmente desea hacer es reflejar toda la clase Person
para obtener una lista de los campos de esa clase:
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.
}
De acuerdo, en función de su edición, tengo algo más para usted.
Puede encontrar el nombre de los campos que están decorados con su atributo en tiempo de ejecución como este:
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
}
También puede usar indicadores de enlace en la primera llamada GetMembers () para limitarlo a los campos, si lo desea.
t.GetField("city", BindingFlags.Public | BindingFlags.Instance);
o puede llamar a GetFields () para obtener todos los campos