2009-12-31 16 views
9

Decir que tengo una clase con una propiedadun nombre de propiedad en .NET

Public Class MyClass 
    Public Property MyItem() as Object 
     .... 
    End Property 
End Class 

tengo que pasar el nombre de la propiedad a una llamada de función. (Por favor, no pregunte por qué debería hacerse de esta manera, es un marco de terceros). Por ejemplo

SomeFunc("MyItem") 

Pero lo que me gustaría hacer es cambiar la cadena en un parámetro fuertemente tipado. Es decir, si el nombre de la propiedad se renombra o se cambia, se debe reflejar aquí también.

Así que algo de este tipo:

Dim objectForStrongTyping as New MyClass() 
SomeFunc(objectForStrongTyping.MyItem().Name()) 

Estoy seguro de que esto no va a funcionar. ¿Hay alguna manera de hacer esta tipificación fuerte? (C# o VB.NET, cualquier cosa es fresco)

Respuesta

20

Aquí hay una solución que usa clases de System.Linq.Expressions.

static MemberInfo GetMemberInfo<TObject, TProperty>(
    Expression<Func<TObject, TProperty>> expression 
) { 
    var member = expression.Body as MemberExpression; 
    if (member != null) { 
     return member.Member; 
    } 

    throw new ArgumentException("expression"); 
} 

sólo un tiro esto en una clase en alguna parte (ExpressionHelper?).

Uso:

class SomeClass { 
    public string SomeProperty { get; set; } 
} 

MemberInfo member = GetMemberInfo((SomeClass s) => s.SomeProperty); 
Console.WriteLine(member.Name); // prints "SomeProperty" on the console 
+0

+1 ¡Para un excelente recorte rápido! –

+0

Bien, ¿se puede envolver en una extensión? – ChaosPandion

0

siempre se puede utilizar una clase estática que contiene string constantes en lugar de pasar en un literal string:

public static class ObjectForStrongTyping 
{ 
    public const string MyItem = "MyItem"; 
    public const string MyOtherItem = "MyOtherItem"; 
    // ... 
} 

Su código se convertiría entonces en:

SomeFunc(ObjectForStrongTyping.MyItem); 
+0

+1: mientras que no es una s "automático" como las soluciones LINQ y typeof (...), es súper simple y el mantenimiento es mínimo. – Juliet

1

Si sólo hay una propiedad que puede hacer esto - obtener la información de la propiedad en la primera propiedad de la clase:

//C# syntax 
typeof(MyClass).GetProperties()[0].Name; 

'VB syntax 
GetType(MyClass).GetProperties()(0).Name 

EDIT Resulta que, donde puede usar expresiones, también puede usar projection para este tipo de reflejo (código C#).

public static class ObjectExtensions { 
    public static string GetVariableName<T>(this T obj) { 
     System.Reflection.PropertyInfo[] objGetTypeGetProperties = obj.GetType().GetProperties(); 

     if(objGetTypeGetProperties.Length == 1) 
      return objGetTypeGetProperties[0].Name; 
     else 
      throw new ArgumentException("object must contain one property"); 
    } 
} 

class Program { 
    static void Main(string[] args) { 
     Console.WriteLine(Console.WriteLine(new { (new MyClass()).MyItem}.GetVariableName());); 
    } 
} 

Con esta solución, la clase puede tener cualquier cantidad de propiedades, usted podría obtener cualquier otro nombre.

+0

Ouch. Ahora, en lugar de preocuparse por el cambio de nombres de las propiedades, ¿tenemos que preocuparnos de agregar/reordenar las propiedades? – Greg

+0

Sí, está bastante sucio, pero según el OP, la clase solo tiene una propiedad. Ver edición para una mejor solución. –

+0

Con el EDIT, esta es una respuesta muy interesante. El método de proyección es bueno. ¿Hay alguna recomendación para la que tenga más sentido usar? En VB.Net, la sintaxis de tipo anónimo es más concisa que la sintaxis lambda. – mrmillsy

3

Esta solución funciona tanto en C# y VB.NET, pero la sintaxis VB.NET para las funciones lambda no es tan limpio, lo que probablemente haría que esta solución menos atractivo en VB. Mis ejemplos estarán en C#.

Puede lograr el efecto deseado mediante las características de la función lambda y el árbol de expresión de C# 3. Básicamente, podría escribir una función de contenedor llamado SomeFuncHelper y lo llaman así:

MyClass objForStrongTyping = new MyClass(); 
SomeFuncHelper(() => objForStrongTyping.MyItem); 

SomeFuncHelper se implementa como sigue:

void SomeFuncHelper(Expression<Func<object>> expression) 
{ 
    string propertyName = /* get name by examining expression */; 
    SomeFunc(propertyName); 
} 

La expresión lambda () => objForStrongTyping.MyItem se traduce en un objeto de expresión que se pasa a SomeFuncHelper. SomeFuncHelper examina Expression, extrae el nombre de la propiedad y llama SomeFunc.En mi prueba rápida, el siguiente código funciona para recuperar el nombre de la propiedad, SomeFuncHelper suponiendo siempre se llama como se muestra arriba (es decir () => someObject.SomeProperty):

propertyName = ((MemberExpression) ((UnaryExpression) expression.Body).Operand).Member.Name; 

es probable que quiera leer sobre los árboles de expresión y trabajar con el código para hacerlo más robusto, pero esa es la idea general.

Actualización: Esto es similar a la solución de Jason, pero permite la expresión lambda dentro de la llamada helper-función sea un poco más simple (() => obj.Property en lugar de (SomeType obj) => obj.Property). Por supuesto, esto es solo más simple si ya tiene una instancia del tipo sentado.

0

La mejor solución que creo es generar constantes estáticas usando T4 (por ejemplo, T4MVC).

public static class StaticSampleClass 
{ 
    public const string MyProperty = "MyProperty"; 
} 

Créanme cuando tenga muchas llamadas a la reflexión y la expresión linq está reduciendo el rendimiento de su aplicación.

Lo malo es que T4 se ha ido en el núcleo neto. :(

Lo bueno es que en C# 6.0 u puede utilizar nameof(SampleClass.MyProperty)

En el peor de los casos u puede utilizar el siguiente ejemplo:

using System.Linq.Expressions; 

namespace ConsoleApp1 
{ 
    public static class Helper 
    { 
     public static string GetPropertyName<T>(Expression<Func<T, object>> propertyExpression) 
     { 
      var member = propertyExpression.Body as MemberExpression; 
      if (member != null) 
       return member.Member.Name; 
      else 
       throw new ArgumentNullException("Property name not found."); 
     } 
     public static string GetPropertyName<T>(this T obj, Expression<Func<T, object>> propertyExpression) 
     { 
      return GetPropertyName(propertyExpression); 
     } 
    } 

    public class SampleClass 
    { 
     public string MyProperty { get; set; } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      // Property name of type 
      Console.WriteLine(Helper.GetPropertyName<SampleClass>(x => x.MyProperty)); 

      // Property name of instance 
      var someObject = new SampleClass(); 
      Console.WriteLine(someObject.GetPropertyName(x => x.MyProperty)); 

      Console.ReadKey(); 
     } 
    } 
} 

resultados de rendimiento (1 millón de veces llamada):

StaticSampleClass.MyProperty - de 8 ms

nameof(SampleClass.MyProperty) - 8 ms

Helper.GetPropertyName<SampleClass>(x => x.MyProperty) - 2000 ms

3

En C# 6.0 Hay una nueva característica llamada nameof. Básicamente se puede hacer esto:

var name = nameof(MyClass.MyItem); 

Mirando convertidor de código Telerik de C# a VB parece que este es el equivalente VB:

Dim name = nameof([MyClass].MyItem) 

para que pueda hacer lo siguiente:

SomeFunc(nameof(MyClass.MyItem)); 

Aquí está la referencia a la documentación de Microsoft: https://docs.microsoft.com/en-us/dotnet/articles/csharp/language-reference/keywords/nameof

Cuestiones relacionadas