2008-11-19 18 views
43

Ha habido una gran sensación de incluir un operador nameof en C#. Como ejemplo de cómo funcionaría este operador, nameof(Customer.Name) devolvería la cadena "Name".¿Solución alternativa para la falta de operador 'nameof' en C# para el enlace de datos seguro?

Tengo un objeto de dominio. Y tengo que atarlo. Y necesito nombres de propiedades como cadenas entonces. Y quiero que sean seguros de tipo.

Recuerdo que me encontré con una solución en .NET 3.5 que proporcionaba la funcionalidad de nameof e implicaba expresiones lambda. Sin embargo, no he podido encontrar esta solución. ¿Alguien puede proporcionarme esa solución?

También estoy interesado en una forma de implementar la funcionalidad de nameof en .NET 2.0 si eso es posible.

+0

ver también http://stackoverflow.com/questions/1329138/how-to -make-databinding-type-safe-and-support-refactoring –

+2

¡Este problema ahora se resuelve en tiempo de compilación! El operador ['nameof'] (https://msdn.microsoft.com/en-us/magazine/dn802602.aspx) se implementó en C# 6.0 con .NET 4.6 y VS2015 en julio de 2015. Las siguientes respuestas siguen siendo válidas para C# <6.0. – Mike

Respuesta

77

Este código hace básicamente que:

class Program 
{ 
    static void Main() 
    { 
     var propName = Nameof<SampleClass>.Property(e => e.Name); 

     Console.WriteLine(propName); 
    } 
} 

public class Nameof<T> 
{ 
    public static string Property<TProp>(Expression<Func<T, TProp>> expression) 
    { 
     var body = expression.Body as MemberExpression; 
     if(body == null) 
      throw new ArgumentException("'expression' should be a member expression"); 
     return body.Member.Name; 
    } 
} 

(Por supuesto, es 3,5 código ...)

+14

Tenga en cuenta que existe una penalización de rendimiento. Los objetos de expresión son bastante caros de crear. Llamar a un 'Foo (Expresión >)' es 200 veces más lento que un antiguo 'Foo (string propName)'. Por favor, vote por un [nombre de operador en tiempo de compilación] (http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2427047-add-nameof-operator-in-c-). –

+0

Tenga en cuenta que este código debe haberse compilado en VS2015 para poder usar Expression.Body – BillW

4

La solución consiste en utilizar un árbol de expresiones y separar ese árbol de expresiones para encontrar el correspondiente MemberInfo. Hay un poco más de detalles y comentarios en this note (aunque no es el código para sacar el miembro, eso está en otra pregunta de SO en algún lado, creo).

Desafortunadamente, como los árboles de expresiones no existen en .NET 2.0, realmente no hay equivalente.

Una solución para evitar errores tipográficos es tener un conjunto de accesadores que obtienen el correspondiente PropertyInfo para una propiedad en particular, y probarlos en la unidad. Ese sería el único lugar que tenía la cuerda. Esto evitaría la duplicación y facilitaría la refactorización, pero es un poco draconiano.

+0

es esto lo que estás buscando? http://imaginarydevelopment.blogspot.com/2009/10/compile-time-safe-databinding.html hace referencia a este http://stackoverflow.com/questions/1329138/how-to-make-databinding-type-safe- y-soporte-refactoring – Maslow

6

Mientras reshefm y Jon Skeet muestran la forma correcta de hacer esto usando expresiones, debe ser Cabe destacar que hay una forma más económica de hacerlo para los nombres de métodos:

Envuelva a un delegado en su método, obtenga el MethodInfo, y listo. Aquí hay un ejemplo:

private void FuncPoo() 
{ 
} 

... 

// Get the name of the function 
string funcName = new Action(FuncPoo).Method.Name; 

Desafortunadamente, esto funciona solo por los métodos; no funciona para las propiedades, ya que no puede tener delegados en los métodos getter o setter de la propiedad. (Parece una limitación tonta, IMO.)

+0

Acepto. Tonta limitación No creo que sea demasiado difícil para el compilador saber si es el getter o el setter y le permite delegarlo. – reshefm

4

Una extensión a lo reshefm hizo, que simplifica el uso del operador nombredel(), y da los nombres de los métodos y los miembros de la clase y métodos así:

/// <summary> 
/// Provides the <see cref="nameof"/> extension method that works as a workarounds for a nameof() operator, 
/// which should be added to C# sometime in the future. 
/// </summary> 
public static class NameOfHelper 
{ 
    /// <summary> 
    /// Returns a string represantaion of a property name (or a method name), which is given using a lambda expression. 
    /// </summary> 
    /// <typeparam name="T">The type of the <paramref name="obj"/> parameter.</typeparam> 
    /// <typeparam name="TProp">The type of the property (or the method's return type), which is used in the <paramref name="expression"/> parameter.</typeparam> 
    /// <param name="obj">An object, that has the property (or method), which its name is returned.</param> 
    /// <param name="expression">A Lambda expression of this pattern: x => x.Property <BR/> 
    /// Where the x is the <paramref name="obj"/> and the Property is the property symbol of x.<BR/> 
    /// (For a method, use: x => x.Method()</param> 
    /// <returns>A string that has the name of the given property (or method).</returns> 
    public static string nameof<T, TProp>(this T obj, Expression<Func<T, TProp>> expression) 
    { 
     MemberExpression memberExp = expression.Body as MemberExpression; 
     if (memberExp != null) 
      return memberExp.Member.Name; 

     MethodCallExpression methodExp = expression.Body as MethodCallExpression; 
     if (methodExp != null) 
      return methodExp.Method.Name; 

     throw new ArgumentException("'expression' should be a member expression or a method call expression.", "expression"); 
    } 

    /// <summary> 
    /// Returns a string represantaion of a property name (or a method name), which is given using a lambda expression. 
    /// </summary> 
    /// <typeparam name="TProp">The type of the property (or the method's return type), which is used in the <paramref name="expression"/> parameter.</typeparam> 
    /// <param name="expression">A Lambda expression of this pattern:() => x.Property <BR/> 
    /// Where Property is the property symbol of x.<BR/> 
    /// (For a method, use:() => x.Method()</param> 
    /// <returns>A string that has the name of the given property (or method).</returns> 
    public static string nameof<TProp>(Expression<Func<TProp>> expression) 
    { 
     MemberExpression memberExp = expression.Body as MemberExpression; 
     if (memberExp != null) 
      return memberExp.Member.Name; 

     MethodCallExpression methodExp = expression.Body as MethodCallExpression; 
     if (methodExp != null) 
      return methodExp.Method.Name; 

     throw new ArgumentException("'expression' should be a member expression or a method call expression.", "expression"); 
    } 
} 

utilizarlo:

static class Program 
{ 
    static void Main() 
    { 
     string strObj = null; 
     Console.WriteLine(strObj.nameof(x => x.Length)); //gets the name of an object's property. 
     Console.WriteLine(strObj.nameof(x => x.GetType())); //gets the name of an object's method. 
     Console.WriteLine(NameOfHelper.nameof(() => string.Empty)); //gets the name of a class' property. 
     Console.WriteLine(NameOfHelper.nameof(() => string.Copy(""))); //gets the name of a class' method. 
    } 
} 
1

La respuesta de reshefm es bastante bueno, pero esto es un poco más simple API IMO: ejemplo

Uso: NameOf.Property(() => new Order().Status)

using System; 
using System.Diagnostics.Contracts; 
using System.Linq.Expressions; 

namespace AgileDesign.Utilities 
{ 
public static class NameOf 
{ 
    ///<summary> 
    /// Returns name of any method expression with any number of parameters either void or with a return value 
    ///</summary> 
    ///<param name = "expression"> 
    /// Any method expression with any number of parameters either void or with a return value 
    ///</param> 
    ///<returns> 
    /// Name of any method with any number of parameters either void or with a return value 
    ///</returns> 
    [Pure] 
    public static string Method(Expression<Action> expression) 
    { 
     Contract.Requires<ArgumentNullException>(expression != null); 

     return ((MethodCallExpression)expression.Body).Method.Name; 
    } 

    ///<summary> 
    /// Returns name of property, field or parameter expression (of anything but method) 
    ///</summary> 
    ///<param name = "expression"> 
    /// Property, field or parameter expression 
    ///</param> 
    ///<returns> 
    /// Name of property, field, parameter 
    ///</returns> 
    [Pure] 
    public static string Member(Expression<Func<object>> expression) 
    { 
     Contract.Requires<ArgumentNullException>(expression != null); 

     if(expression.Body is UnaryExpression) 
     { 
      return ((MemberExpression)((UnaryExpression)expression.Body).Operand).Member.Name; 
     } 
     return ((MemberExpression)expression.Body).Member.Name; 
    } 
    } 
} 

código completo está aquí: http://agiledesignutilities.codeplex.com/SourceControl/changeset/view/b76cefa4234a#GeneralPurpose/NameOf.cs

4

A menos que alguien cambia de opinión, el operador nameof se ve como si viniera en C# 6. Estas son las notas de la reunión de diseño al respecto:

https://roslyn.codeplex.com/discussions/552376

https://roslyn.codeplex.com/discussions/552377

+0

He estado esperando esto por tanto tiempo. Qué bueno ver que se implemente. –

+0

Y aquí está la documentación oficial para el operador ['nameof'] (https://msdn.microsoft.com/en-us/library/dn986596.aspx) que de hecho estuvo disponible en C# 6.0 (y Visual Studio 2015) . – DavidRR

2

La solución aceptada es agradable, sencillo y elegante.

Sin embargo, crear un árbol de expresiones es costoso, y necesito toda la ruta de la propiedad.

Así que lo cambié un poco. No es elegante en absoluto, pero es sencillo y funciona bien en la mayoría de los casos:

public static string Property<TProp>(Expression<Func<T, TProp>> expression) 
{ 
    var s = expression.Body.ToString(); 
    var p = s.Remove(0, s.IndexOf('.') + 1); 
    return p; 
} 

Ejemplo:

? Nameof<DataGridViewCell>.Property(c => c.Style.BackColor.A); 
"Style.BackColor.A" 
+0

que crearon el método genérico: pública PropertyPath cadena estática (obj este T, la expresión > expresión) { var s = expression.Body.ToString(); var p = s.Eliminar (0, s.IndexOf ('.') + 1); return p; } –

+1

De la documentación para el operador ['nameof'] (https://msdn.microsoft.com/en-us/library/dn986596.aspx) (nuevo en C# 6.0), en la sección ** Observaciones ** : * Si necesita obtener el nombre completo, puede usar la expresión * *** typeof *** * junto con * *** nameof *** *. * – DavidRR

Cuestiones relacionadas