Obtener el nombre de la propiedad como una cadena


(Ver abajo la solución que he creado usando la respuesta que he aceptado)

Estoy tratando de mejorar el mantenimiento de algún código que involucre reflexión. La aplicación tiene una interfaz remota. NET que expone (entre otras cosas) un método llamado Execute para acceder a partes de la aplicación que no están incluidas en su interfaz remota publicada.

Así es como la aplicación designa las propiedades (una estática en este ejemplo) que están destinadas a ser accesibles a través de Execute:

RemoteMgr.ExposeProperty("SomeSecret", typeof(SomeClass), "SomeProperty");

Para que un usuario remoto pueda llamada:

string response = remoteObject.Execute("SomeSecret");

Y la aplicación usaría reflexión para encontrar alguna clase.someProperty y devuelve su valor como una cadena.

Desafortunadamente, si alguien cambia el nombre de someProperty y olvida cambiar el 3er parm de ExposeProperty(), rompe este mecanismo.

Necesito el equivalente de:

SomeClass.SomeProperty.GetTheNameOfThisPropertyAsAString()

Para usar como el 3er parm en ExposeProperty, las herramientas de refactorización se encargarían de los cambios de nombre.

¿Hay alguna manera de hacer esto? Gracias de antemano.

Bien, esto es lo que Terminé creando (basado en la respuesta que seleccioné y la pregunta a la que hizo referencia):

// <summary>
// Get the name of a static or instance property from a property access lambda.
// </summary>
// <typeparam name="T">Type of the property</typeparam>
// <param name="propertyLambda">lambda expression of the form: '() => Class.Property' or '() => object.Property'</param>
// <returns>The name of the property</returns>
public string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
    var me = propertyLambda.Body as MemberExpression;

    if (me == null)
    {
        throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
    }

    return me.Member.Name;
 }

Uso:

// Static Property
string name = GetPropertyName(() => SomeClass.SomeProperty);

// Instance Property
string name = GetPropertyName(() => someObject.SomeProperty);

Ahora con esta capacidad genial, es el momento de simplificar el método ExposeProperty. Pulir los pomos de las puertas es un trabajo peligroso...

Gracias a todos.

Author: Jim C, 2010-05-12

13 answers

Usando GetMemberInfo desde aquí: Recuperando el nombre de la propiedad de la expresión lambda puede hacer algo como esto:

RemoteMgr.ExposeProperty(() => SomeClass.SomeProperty)

public class SomeClass
{
    public static string SomeProperty
    {
        get { return "Foo"; }
    }
}

public class RemoteMgr
{
    public static void ExposeProperty<T>(Expression<Func<T>> property)
    {
        var expression = GetMemberInfo(property);
        string path = string.Concat(expression.Member.DeclaringType.FullName,
            ".", expression.Member.Name);
        // Do ExposeProperty work here...
    }
}

public class Program
{
    public static void Main()
    {
        RemoteMgr.ExposeProperty("SomeSecret", () => SomeClass.SomeProperty);
    }
}
 57
Author: Daniel Renshaw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-04 03:25:48

Con C # 6.0, esto ahora no es un problema como puede hacer:

nameof(SomeProperty)

Esta expresión se resuelve en tiempo de compilación a "SomeProperty".

MSDN documentation of nameof.

 311
Author: James Ko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-22 19:57:07

Hay un conocido truco para extraerlo de la expresión lambda (esto es de la clase PropertyObserver, de Josh Smith, en su fundación MVVM):

    private static string GetPropertyName<TPropertySource>
        (Expression<Func<TPropertySource, object>> expression)
    {
        var lambda = expression as LambdaExpression;
        MemberExpression memberExpression;
        if (lambda.Body is UnaryExpression)
        {
            var unaryExpression = lambda.Body as UnaryExpression;
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else
        {
            memberExpression = lambda.Body as MemberExpression;
        }

        Debug.Assert(memberExpression != null, 
           "Please provide a lambda expression like 'n => n.PropertyName'");

        if (memberExpression != null)
        {
            var propertyInfo = memberExpression.Member as PropertyInfo;

            return propertyInfo.Name;
        }

        return null;
    }

Lo sentimos, a esto le faltaba algo de contexto. Esto era parte de una clase más grande donde TPropertySource es la clase que contiene la propiedad. Podría hacer que la función sea genérica en TPropertySource para extraerla de la clase. Recomiendo echar un vistazo al código completo de la MVVM Foundation.

 16
Author: Dan Bryant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-17 04:40:34

Bien, esto es lo que terminé creando (basado en la respuesta que seleccioné y la pregunta a la que hizo referencia):

// <summary>
// Get the name of a static or instance property from a property access lambda.
// </summary>
// <typeparam name="T">Type of the property</typeparam>
// <param name="propertyLambda">lambda expression of the form: '() => Class.Property' or '() => object.Property'</param>
// <returns>The name of the property</returns>

public string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
    var me = propertyLambda.Body as MemberExpression;

    if (me == null)
    {
        throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
    }

    return me.Member.Name;
 }

Uso:

// Static Property
string name = GetPropertyName(() => SomeClass.SomeProperty);

// Instance Property
string name = GetPropertyName(() => someObject.SomeProperty);
 14
Author: Jim C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-29 22:51:28

La clase PropertyInfo debería ayudarte a lograr esto, si he entendido bien.

  1. Tipo.Método GetProperties ()

    PropertyInfo[] propInfos = typeof(ReflectedType).GetProperties();
    propInfos.ToList().ForEach(p => 
        Console.WriteLine(string.Format("Property name: {0}", p.Name));
    

¿Es esto lo que necesitas?

 6
Author: Will Marcouiller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-12 16:22:15

Puede utilizar la reflexión para obtener los nombres reales de las propiedades.

Http://www.csharp-examples.net/reflection-property-names /

Si necesita una forma de asignar un "Nombre de cadena" a una propiedad, ¿por qué no escribe un atributo que pueda reflejar para obtener el nombre de la cadena?

[StringName("MyStringName")]
private string MyProperty
{
    get { ... }
}
 6
Author: Robert Harvey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-12 16:25:07

He modificado su solución para encadenar varias propiedades:

public static string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
    MemberExpression me = propertyLambda.Body as MemberExpression;
    if (me == null)
    {
        throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
    }

    string result = string.Empty;
    do
    {
        result = me.Member.Name + "." + result;
        me = me.Expression as MemberExpression;
    } while (me != null);

    result = result.Remove(result.Length - 1); // remove the trailing "."
    return result;
}

Uso:

string name = GetPropertyName(() => someObject.SomeProperty.SomeOtherProperty);
// returns "SomeProperty.SomeOtherProperty"
 5
Author: hypehuman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-23 00:13:30

Basado en la respuesta que ya está en la pregunta y en este artículo: https://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/ Estoy presentando mi solución a este problema:

public static class PropertyNameHelper
{
    /// <summary>
    /// A static method to get the Propertyname String of a Property
    /// It eliminates the need for "Magic Strings" and assures type safety when renaming properties.
    /// See: http://stackoverflow.com/questions/2820660/get-name-of-property-as-a-string
    /// </summary>
    /// <example>
    /// // Static Property
    /// string name = PropertyNameHelper.GetPropertyName(() => SomeClass.SomeProperty);
    /// // Instance Property
    /// string name = PropertyNameHelper.GetPropertyName(() => someObject.SomeProperty);
    /// </example>
    /// <typeparam name="T"></typeparam>
    /// <param name="propertyLambda"></param>
    /// <returns></returns>
    public static string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
    {
        var me = propertyLambda.Body as MemberExpression;

        if (me == null)
        {
            throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
        }

        return me.Member.Name;
    }
    /// <summary>
    /// Another way to get Instance Property names as strings.
    /// With this method you don't need to create a instance first.
    /// See the example.
    /// See: https://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/
    /// </summary>
    /// <example>
    /// string name = PropertyNameHelper((Firma f) => f.Firmenumsatz_Waehrung);
    /// </example>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="TReturn"></typeparam>
    /// <param name="expression"></param>
    /// <returns></returns>
    public static string GetPropertyName<T, TReturn>(Expression<Func<T, TReturn>> expression)
    {
        MemberExpression body = (MemberExpression)expression.Body;
        return body.Member.Name;
    }
}

Y una Prueba que también muestra el uso, por ejemplo, y las propiedades estáticas:

[TestClass]
public class PropertyNameHelperTest
{
    private class TestClass
    {
        public static string StaticString { get; set; }
        public string InstanceString { get; set; }
    }

    [TestMethod]
    public void TestGetPropertyName()
    {
        Assert.AreEqual("StaticString", PropertyNameHelper.GetPropertyName(() => TestClass.StaticString));

        Assert.AreEqual("InstanceString", PropertyNameHelper.GetPropertyName((TestClass t) => t.InstanceString));
    }
}
 4
Author: Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-02 11:30:11

Pregunta antigua, pero otra respuesta a esta pregunta es crear una función estática en una clase helper que use el CallerMemberNameAttribute.

public static string GetPropertyName([CallerMemberName] String propertyName = null) {
  return propertyName;
}

Y luego usarlo como:

public string MyProperty {
  get { Console.WriteLine("{0} was called", GetPropertyName()); return _myProperty; }
}
 3
Author: Jim Pedid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-23 11:07:02

Puede usar la clase StackTrace para obtener el nombre de la función actual, (o si coloca el código en una función, baje un nivel y obtenga la función que llama).

Véase http://msdn.microsoft.com/en-us/library/system.diagnostics.stacktrace (VS.71).aspx

 0
Author: Sprotty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-12 16:15:20

He estado usando esta respuesta con gran efecto: Obtener la propiedad, como una cadena, de una expresión>

Me doy cuenta de que ya respondí a esta pregunta hace un tiempo. La única ventaja que tiene mi otra respuesta es que funciona para propiedades estáticas. Encuentro la sintaxis en esta respuesta mucho más útil porque no tienes que crear una variable del tipo que quieres reflejar.

 0
Author: hypehuman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:38

Tuve algunas dificultades para usar las soluciones ya sugeridas para mi caso de uso específico, pero finalmente lo descubrí. No creo que mi caso específico sea digno de una nueva pregunta, por lo que estoy publicando mi solución aquí como referencia. (Esto está muy estrechamente relacionado con la cuestión y proporciona una solución para cualquier otra persona con un caso similar al mío).

El código con el que terminé se ve así:

public class HideableControl<T>: Control where T: class
{
    private string _propertyName;
    private PropertyInfo _propertyInfo;

    public string PropertyName
    {
        get { return _propertyName; }
        set
        {
            _propertyName = value;
            _propertyInfo = typeof(T).GetProperty(value);
        }
    }

    protected override bool GetIsVisible(IRenderContext context)
    {
        if (_propertyInfo == null)
            return false;

        var model = context.Get<T>();

        if (model == null)
            return false;

        return (bool)_propertyInfo.GetValue(model, null);
    }

    protected void SetIsVisibleProperty(Expression<Func<T, bool>> propertyLambda)
    {
        var expression = propertyLambda.Body as MemberExpression;
        if (expression == null)
            throw new ArgumentException("You must pass a lambda of the form: 'vm => vm.Property'");

        PropertyName = expression.Member.Name;
    }
}

public interface ICompanyViewModel
{
    string CompanyName { get; }
    bool IsVisible { get; }
}

public class CompanyControl: HideableControl<ICompanyViewModel>
{
    public CompanyControl()
    {
        SetIsVisibleProperty(vm => vm.IsVisible);
    }
}

La parte importante para mí es que en la clase CompanyControl el compilador solo permítanme elegir una propiedad booleana de ICompanyViewModel que hace que sea más fácil para otros desarrolladores hacerlo bien.

La principal diferencia entre mi solución y la respuesta aceptada es que mi clase es genérica y solo quiero hacer coincidir las propiedades del tipo genérico que son booleanas.

 0
Author: bikeman868,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-23 15:36:31

Es cómo lo implementé , la razón detrás es si la clase que desea obtener el nombre de su miembro no es estática, entonces necesita crear una instancia de eso y luego obtener el nombre del miembro. así que genérico aquí viene a ayudar

public static string GetName<TClass>(Expression<Func<TClass, object>> exp)
{
    MemberExpression body = exp.Body as MemberExpression;

    if (body == null)
    {
         UnaryExpression ubody = (UnaryExpression)exp.Body;
         body = ubody.Operand as MemberExpression;
    }

     return body.Member.Name;
}

El uso es así

var label = ClassExtension.GetName<SomeClass>(x => x.Label); //x is refering to 'SomeClass'
 0
Author: Mo Hrad A,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-29 04:20:18