tag propiedad c#

c# - propiedad - Encontrar el nombre de variable pasado a una función



propiedad tag c# (16)

** No. ** No lo creo.

El nombre de variable que utiliza es para su conveniencia y legibilidad. El compilador no lo necesita y simplemente lo descarta si no me equivoco.

Si ayuda, podría definir una nueva clase llamada NamedParameter con atributos Name y Param. A continuación, pasa este objeto como parámetros.

Permítanme usar el siguiente ejemplo para explicar mi pregunta:

public string ExampleFunction(string Variable) { return something; } string WhatIsMyName = "Hello World"''; string Hello = ExampleFunction(WhatIsMyName);

Cuando paso la variable "WhatIsMyName" a la función de ejemplo, deseo poder obtener una cadena del nombre de las variables originales. Quizás algo como:

Variable.OriginalName.ToString()

¿Hay alguna manera de hacer esto?


Bueno, prueba esta clase de utilidad,

public static class Utility { public static Tuple<string, TSource> GetNameAndValue<TSource>(Expression<Func<TSource>> sourceExpression) { Tuple<String, TSource> result = null; Type type = typeof (TSource); Func<MemberExpression, Tuple<String, TSource>> process = delegate(MemberExpression memberExpression) { ConstantExpression constantExpression = (ConstantExpression)memberExpression.Expression; var name = memberExpression.Member.Name; var value = ((FieldInfo)memberExpression.Member).GetValue(constantExpression.Value); return new Tuple<string, TSource>(name, (TSource) value); }; Expression exception = sourceExpression.Body; if (exception is MemberExpression) { result = process((MemberExpression)sourceExpression.Body); } else if (exception is UnaryExpression) { UnaryExpression unaryExpression = (UnaryExpression)sourceExpression.Body; result = process((MemberExpression)unaryExpression.Operand); } else { throw new Exception("Expression type unknown."); } return result; } }

Y el usuario lo quiere

/*ToDo : Test Result*/ static void Main(string[] args) { /*Test : primivit types*/ long maxNumber = 123123; Tuple<string, long> longVariable = Utility.GetNameAndValue(() => maxNumber); string longVariableName = longVariable.Item1; long longVariableValue = longVariable.Item2; /*Test : user define types*/ Person aPerson = new Person() { Id = "123", Name = "Roy" }; Tuple<string, Person> personVariable = Utility.GetNameAndValue(() => aPerson); string personVariableName = personVariable.Item1; Person personVariableValue = personVariable.Item2; /*Test : anonymous types*/ var ann = new { Id = "123", Name = "Roy" }; var annVariable = Utility.GetNameAndValue(() => ann); string annVariableName = annVariable.Item1; var annVariableValue = annVariable.Item2; /*Test : Enum tyoes*/ Active isActive = Active.Yes; Tuple<string, Active> isActiveVariable = Utility.GetNameAndValue(() => isActive); string isActiveVariableName = isActiveVariable.Item1; Active isActiveVariableValue = isActiveVariable.Item2; }


Bueno, tenía un poco de mirada. por supuesto, no puede usar ninguna información de Tipo. Además, el nombre de una variable local no está disponible en tiempo de ejecución porque sus nombres no se compilan en los metadatos del ensamblado.


GateKiller, ¿qué pasa con mi solución? Podrías reescribir tu función trivialmente para usarla (me he tomado la libertad de mejorar la función sobre la marcha):

static string sMessages(Expression<Func<List<string>>> aMessages) { var messages = aMessages.Compile()(); if (messages.Count == 0) { return ""; } StringBuilder ret = new StringBuilder(); string sType = ((MemberExpression)aMessages.Body).Member.Name; ret.AppendFormat("<p class=/"{0}/">", sType); foreach (string msg in messages) { ret.Append(msg); ret.Append("<br />"); } ret.Append("</p>"); return ret.ToString(); }

Llámalo así:

var errors = new List<string>() { "Hi", "foo" }; var ret = sMessages(() => errors);


Gracias por todas las respuestas. Creo que tendré que ir con lo que estoy haciendo ahora.

Para aquellos que querían saber por qué hice la pregunta anterior. Tengo la siguiente función:

string sMessages(ArrayList aMessages, String sType) { string sReturn = String.Empty; if (aMessages.Count > 0) { sReturn += "<p class=/"" + sType + "/">"; for (int i = 0; i < aMessages.Count; i++) { sReturn += aMessages[i] + "<br />"; } sReturn += "</p>"; } return sReturn; }

Le envío una serie de mensajes de error y una clase css que luego se devuelve como una cadena para una página web.

Cada vez que llamo a esta función, tengo que definir sType. Algo como:

output += sMessages(aErrors, "errors");

Como puede ver, mis variables se llaman aErrors y mi clase css se llama errors. Esperaba que mi resfriado supiera qué clase usar según el nombre de la variable que le envié.

De nuevo, gracias por todas las respuestas.


Hacer esto

var myVariable = 123; myVariable.Named(() => myVariable); var name = myVariable.Name(); // use name how you like

o nombrar en código a mano

var myVariable = 123.Named("my variable"); var name = myVariable.Name();

usando esta clase

public static class ObjectInstanceExtensions { private static Dictionary<object, string> namedInstances = new Dictionary<object, string>(); public static void Named<T>(this T instance, Expression<Func<T>> expressionContainingOnlyYourInstance) { var name = ((MemberExpression)expressionContainingOnlyYourInstance.Body).Member.Name; instance.Named(name); } public static T Named<T>(this T instance, string named) { if (namedInstances.ContainsKey(instance)) namedInstances[instance] = named; else namedInstances.Add(instance, named); return instance; } public static string Name<T>(this T instance) { if (namedInstances.ContainsKey(instance)) return namedInstances[instance]; throw new NotImplementedException("object has not been named"); } }

El código probado y más elegante que se me ocurre.


La respuesta corta es no ... a menos que realmente estés muy motivado.

La única forma de hacerlo sería a través de la reflexión y la pila caminando. Tendría que obtener un marco de pila, averiguar el paradero en la función de llamada desde donde se invocó y luego usar el CodeDOM para buscar la parte correcta del árbol para ver cuál era la expresión.

Por ejemplo, ¿qué pasaría si la invocación fuera ExampleFunction ("a" + "b")?


Lo que quiere no es posible directamente, pero puede usar expresiones en C # 3.0:

public void ExampleFunction(Expression<Func<string, string>> f) { Console.WriteLine((f.Body as MemberExpression).Member.Name); } ExampleFunction(x => WhatIsMyName);

Tenga en cuenta que esto se basa en un comportamiento no especificado y, aunque funciona en los compiladores actuales C # y VB de Microsoft, y en el compilador C # de Mono, no hay garantía de que esto no deje de funcionar en versiones futuras.


No, pero cada vez que te encuentres haciendo cosas extremadamente complejas como esta, es posible que quieras volver a pensar tu solución. Recuerde que el código debería ser más fácil de leer de lo que era escribir.


No. Una referencia a su variable de cadena pasa a la función, no incluye ningún metadato inherente. Incluso la reflexión no te sacará de la raya aquí: trabajar hacia atrás desde un solo tipo de referencia no te proporciona suficiente información para hacer lo que tienes que hacer.

¡Mejor vuelve a la mesa de dibujo en este!

rp


Puede usar el reflejo para obtener todas las propiedades de un objeto, que recorrerlo y obtener el valor de la propiedad donde el nombre (de la propiedad) coincide con el parámetro pasado.


Sé que esta es una pregunta antigua, pero en C # 6.0 introducen el nombre de Operator que debería resolver este problema. El nombre del operador resuelve el nombre de la variable pasada a él.

El uso para su caso se vería así:

public string ExampleFunction(string variableName) { //Construct your log statement using c# 6.0 string interpolation return $"Error occurred in {variableName}"; } string WhatIsMyName = "Hello World"''; string Hello = ExampleFunction(nameof(WhatIsMyName));

Una gran ventaja es que se realiza en tiempo de compilación,

El nombre de expresión es una constante. En todos los casos, nameof (...) se evalúa en tiempo de compilación para producir una cadena. Su argumento no se evalúa en tiempo de ejecución, y se considera código inalcanzable (sin embargo, no emite un aviso de "código inalcanzable").

Más información se puede encontrar here

Versión anterior de C 3.0 y superior
Para construir sobre la respuesta de Nawfals

GetParameterName2(new { variable }); //Hack to assure compiler warning is generated specifying this method calling conventions [Obsolete("Note you must use a single parametered AnonymousType When Calling this method")] public static string GetParameterName<T>(T item) where T : class { if (item == null) return string.Empty; return typeof(T).GetProperties()[0].Name; }


System.Environment.StackTrace le dará una cadena que incluye la pila de llamadas actual. Podría analizar eso para obtener la información, que incluye los nombres de las variables para cada llamada.


Tres maneras:

1) Algo sin reflexión en absoluto:

GetParameterName1(new { variable }); public static string GetParameterName1<T>(T item) where T : class { if (item == null) return string.Empty; return item.ToString().TrimStart(''{'').TrimEnd(''}'').Split(''='')[0].Trim(); }

2) Utiliza la reflexión, pero esto es mucho más rápido que otros dos.

GetParameterName2(new { variable }); public static string GetParameterName2<T>(T item) where T : class { if (item == null) return string.Empty; return typeof(T).GetProperties()[0].Name; }

3) El más lento de todos, no uso.

GetParameterName3(() => variable); public static string GetParameterName3<T>(Expression<Func<T>> expr) { if (expr == null) return string.Empty; return ((MemberExpression)expr.Body).Member.Name; }

Para obtener un nombre y valor de parámetro combinado, puede ampliar estos métodos. Por supuesto, es fácil obtener valor si pasa el parámetro por separado como otro argumento, pero eso no es elegante. En lugar:

1)

public static string GetParameterInfo1<T>(T item) where T : class { if (item == null) return string.Empty; var param = item.ToString().TrimStart(''{'').TrimEnd(''}'').Split(''=''); return "Parameter: ''" + param[0].Trim() + "'' = " + param[1].Trim(); }

2)

public static string GetParameterInfo2<T>(T item) where T : class { if (item == null) return string.Empty; var param = typeof(T).GetProperties()[0]; return "Parameter: ''" + param.Name + "'' = " + param.GetValue(item, null); }

3)

public static string GetParameterInfo3<T>(Expression<Func<T>> expr) { if (expr == null) return string.Empty; var param = (MemberExpression)expr.Body; return "Parameter: ''" + param.Member.Name + "'' = " + ((FieldInfo)param.Member).GetValue(((ConstantExpression)param.Expression).Value); }

1 y 2 son de velocidad comparable ahora, 3 es de nuevo lento.


¡Sí! Es posible. He estado buscando una solución para esto durante mucho tiempo y finalmente he encontrado un truco que lo resuelve (es un poco desagradable). No recomendaría usar esto como parte de tu programa y solo creo que funciona en modo de depuración. Para mí esto no importa ya que solo lo uso como una herramienta de depuración en mi clase de consola, así que puedo hacer lo siguiente:

int testVar = 1; bool testBoolVar = True; myConsole.Writeline(testVar); myConsole.Writeline(testBoolVar);

la salida a la consola sería:

testVar: 1 testBoolVar: True

Aquí está la función que uso para hacer eso (sin incluir el código de envoltura para mi clase de consola).

public Dictionary<string, string> nameOfAlreadyAcessed = new Dictionary<string, string>(); public string nameOf(object obj, int level = 1) { StackFrame stackFrame = new StackTrace(true).GetFrame(level); string fileName = stackFrame.GetFileName(); int lineNumber = stackFrame.GetFileLineNumber(); string uniqueId = fileName + lineNumber; if (nameOfAlreadyAcessed.ContainsKey(uniqueId)) return nameOfAlreadyAcessed[uniqueId]; else { System.IO.StreamReader file = new System.IO.StreamReader(fileName); for (int i = 0; i < lineNumber - 1; i++) file.ReadLine(); string varName = file.ReadLine().Split(new char[] { ''('', '')'' })[1]; nameOfAlreadyAcessed.Add(uniqueId, varName); return varName; } }


static void Main(string[] args) { Console.WriteLine("Name is ''{0}''", GetName(new {args})); Console.ReadLine(); } static string GetName<T>(T item) where T : class { var properties = typeof(T).GetProperties(); Enforce.That(properties.Length == 1); return properties[0].Name; }

Más detalles están en esta publicación de blog .