2008-09-16 15 views
36

me deja utilizar el siguiente ejemplo para explicar mi pregunta:Encontrar el nombre de la variable pasa a una función

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, quiero ser capaz de obtener una cadena del nombre de las variables originales. Tal vez algo como:

Variable.OriginalName.ToString() 

¿Hay alguna manera de hacer esto?

+2

Hmm, ¿por qué querrías hacer eso? Solo necesito entender la lógica detrás de esto –

+0

Creo que alguien quería lo mismo en Ruby. Podrías buscar y ver si tiene alguna indicación sobre cómo hacerlo. – Gishu

+7

Este es un tema recurrente que me vuelve loco. Una pregunta intelectual que se usará para hacer algo, y en lugar de una respuesta ... la gente quiere saber por qué quieres hacer ... Hay suficiente lógica para responder la pregunta ... –

Respuesta

-6

** No. ** No lo creo.

El nombre de la variable que utiliza es para su conveniencia y legibilidad. El compilador no lo necesita & 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.

+11

El duplicado más reciente de esta pregunta original puede tener una respuesta posible de Sí: http://stackoverflow.com/questions/9801624/get-name-of-a-variable-or-parameter –

+4

@alfa, la respuesta que usted '' '' linked to '' da el nombre del parámetro, no el nombre del argumento pasado a la función como se pide en esta pregunta. –

+2

Los argumentos reales son expresiones evaluadas, no variables. –

49

Lo que queremos no es posible directamente, sino que puede utilizar expresiones en C# 3.0:

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

ExampleFunction(x => WhatIsMyName); 

en cuenta que esto se basa en el comportamiento no especificado y mientras que hace el trabajo en el de Microsoft actual C# y compiladores de VB, y en el compilador C# de Mono, no hay garantía de que esto no deje de funcionar en versiones futuras.

+0

¿Alguna forma de hacer esto con una propiedad en lugar de una variable local? Gracias. – pbz

+2

A los efectos de una 'Expresión', una variable local en realidad * es * una propiedad (' .Member.Name' - esto es una consecuencia directa del cierre creado por el compilador para implementar la expresión lambda) por lo que el código anterior debería también trabajo para las propiedades. –

+1

Esta respuesta se basa en un comportamiento no estandarizado del compilador de Microsoft C#, y podría romperse bajo otros compiladores o versiones futuras. Consulte mi [pregunta] (http://stackoverflow.com/q/11063502/1149773) y [respuesta] (http://stackoverflow.com/a/11071271/1149773) sobre el tema. – Douglas

-1

La respuesta corta es no ... a menos que realmente esté 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é ocurre si la invocación fue ExampleFunction ("a" + "b")?

0

No. Una referencia a su variable de cadena se pasa a la función - no hay ningún metadato inherente al respecto incluido. 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 al diseño en este caso!

rp

0

Se podría utilizar la reflexión para obtener todas las propiedades de un objeto, de bucle a través de él, y obtener el valor de la propiedad donde el nombre (de la propiedad) coincide con la aprobada en el parámetro.

-1

Bueno, tenía un poco de aspecto. por supuesto no puede usar ningún tipo de información. 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.

4

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

2

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.

0

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; 
} 

lo 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 así como:

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

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

Nuevamente, gracias por todas las respuestas.

0

GateKiller, ¿qué pasa con mi solución? Se podría reescribir su función trivialmente utilizarla (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(); 
} 

llamada así:

var errors = new List<string>() { "Hi", "foo" }; 
var ret = sMessages(() => errors); 
16
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; 
} 

encontrar más detalles en this blog post.

+3

El enlace está roto, aunque la solución se ve realmente ordenada. ¿Quizás podría agregar algunos detalles aquí? –

+2

@ Clément He arreglado el enlace, ¡salud! – Theraot

+0

var variableName = "valor"; result = (new {variableName}). GetType(). GetProperties() [0] .Name; ** new {variableName} ** crear clase anónima con propiedad variableName = "value", de esa manera .GetProperties() [0] .Name obtener el nombre de esta propiedad – prampe

10

de 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 lo use.

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 de ello:

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.

1

Intento bien 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 que tiene gusto

/*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; 
    } 
2

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 sólo lo uso como una herramienta de depuración en mi clase de la consola para que pueda hacer:

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

la salida de la consola sería:

testVar: 1 
testBoolVar: True 

Aquí es el función que utilizo para hacer eso (no incluyendo 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; 
     } 
    } 
0

haga esto

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

o nombrando en el 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"); 
    }   
} 

probado Código y más elegante que pueda ocurrir.

+0

Personalmente prefiero nombrar a mano como usar la opción de expresión significa que la variable se refiere a sí misma, lo que hace que los ayudantes IDE intellisense destaquen las referencias a la variable utilizada. Un poco más lento pero generalmente más legible. – kernowcode

+0

pero, si usa nombres de variables realmente descriptivos, el primero sería muy útil. – kernowcode

+0

esto no funciona si has nombrado() 2 variables que hacen referencia a la misma instancia. Ver [este] (https://dotnetfiddle.net/lVTIFg) ejemplo –

30

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

Uso para su caso podría tener este aspecto:

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 ventaja importante es que se realiza en tiempo de compilación,

La expresión nombredel 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 Nawfals responden

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; 
} 
+2

Esta es la respuesta correcta para cualquier persona que se haya mudado a C# 6. – Hypnovirus

+13

Lo sentimos, pero esta respuesta es incorrecta. Su ejemplo de 'nameof (s)' simplemente devuelve la cadena 's' y no el nombre de la variable de parámetros con los que se llamó a la función f. En la pregunta original pasaría 'Variable' en' ArgumentNullException' en lugar de 'WhatsMyName'. – Soko

+1

@Soko el propósito era mostrar cómo usar el nombre del operador, pero he hecho modificaciones para reflejar mejor la pregunta –

Cuestiones relacionadas