web-dev-qa-db-fra.com

Obtenir le nom de la propriété sous forme de chaîne

(Voir la solution ci-dessous que j'ai créée en utilisant la réponse que j'ai acceptée)

J'essaie d'améliorer la maintenabilité de certains codes impliquant une réflexion. L'application dispose d'une interface .NET Remoting qui expose (entre autres choses) une méthode appelée Exécuter pour accéder à des parties de l'application non incluses dans son interface distante publiée. 

Voici comment l'application désigne les propriétés (statiques dans cet exemple) censées être accessibles via Execute:

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

Ainsi, un utilisateur distant peut appeler: 

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

et l'application utiliserait la réflexion pour trouver SomeClass.SomeProperty et renvoyer sa valeur sous forme de chaîne.

Malheureusement, si quelqu'un renomme SomeProperty et oublie de changer le troisième paramètre d'ExposeProperty (), il rompt ce mécanisme.

J'ai besoin de l'équivalent de: 

SomeClass.SomeProperty.GetTheNameOfThisPropertyAsAString()

à utiliser comme troisième paramètre dans ExposeProperty afin que les outils de refactoring s’occupent des renommements.

Y a-t-il un moyen de faire cela? Merci d'avance.

Ok, voici ce que j'ai fini par créer (en fonction de la réponse que j'ai sélectionnée et de la question à laquelle il a fait référence):

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

Usage:

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

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

Maintenant, avec cette possibilité intéressante, il est temps de simplifier la méthode ExposeProperty. Polir les poignées de porte est un travail dangereux ...

Merci tout le monde.

169
Jim C

À l'aide de GetMemberInfo à partir d'ici: Récupération du nom de la propriété de l'expression lambda vous pouvez faire quelque chose comme ceci:

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);
    }
}
59
Daniel Renshaw

Avec C # 6.0, ce n'est plus un problème, comme vous pouvez le faire:

nameof(SomeProperty)

Cette expression est résolue au moment de la compilation en "SomeProperty".

Documentation MSDN de nameof .

346
James Ko

Il existe un hack bien connu pour l'extraire de l'expression lambda (celle-ci provient de la classe PropertyObserver de Josh Smith dans sa fondation 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;
    }

Désolé, il manquait un peu de contexte. Cela faisait partie d'une classe plus grande où TPropertySource est la classe contenant la propriété. Vous pouvez rendre la fonction générique dans TPropertySource pour l'extraire de la classe. Je vous recommande de consulter l'intégralité du code de MVVM Foundation .

17
Dan Bryant

Ok, voici ce que j'ai finalement créé (en fonction de la réponse que j'ai sélectionnée et de la question à laquelle il a fait référence):

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

Usage:

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

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

La classe PropertyInfo devrait vous aider à atteindre cet objectif, si je comprends bien.

  1. Méthode Type.GetProperties ()

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

Est-ce ce dont vous avez besoin?

7
Will Marcouiller

Vous pouvez utiliser Reflection pour obtenir les noms réels des propriétés.

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

Si vous avez besoin d'un moyen d'attribuer un "Nom de chaîne" à une propriété, pourquoi ne pas écrire un attribut sur lequel vous pouvez réfléchir pour obtenir le nom de la chaîne?

[StringName("MyStringName")]
private string MyProperty
{
    get { ... }
}
6
Robert Harvey

J'ai modifié votre solution pour enchaîner plusieurs propriétés:

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

Usage:

string name = GetPropertyName(() => someObject.SomeProperty.SomeOtherProperty);
// returns "SomeProperty.SomeOtherProperty"
5
hypehuman

D'après la réponse qui se trouve déjà dans la question et sur cet article: https://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/ Je présente ma solution à ce problème:

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

Et un test qui montre également l'utilisation pour l'instance et les propriétés statiques:

[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
Thomas

Ancienne question, mais une autre réponse à cette question consiste à créer une fonction statique dans une classe d'assistance qui utilise CallerMemberNameAttribute.

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

Et puis l'utiliser comme:

public string MyProperty {
  get { Console.WriteLine("{0} was called", GetPropertyName()); return _myProperty; }
}
3
Jim Pedid

c'est la façon dont je l'ai implémenté, la raison derrière est que si la classe que vous voulez obtenir le nom de son membre n'est pas statique, vous devez créer un instanse de cela et ensuite obtenir le nom du membre. si générique vient ici pour aider

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

l'utilisation est comme ça 

var label = ClassExtension.GetName<SomeClass>(x => x.Label); //x is refering to 'SomeClass'
0
Mo Hrad A

Vous pouvez utiliser la classe StackTrace pour obtenir le nom de la fonction en cours (ou si vous insérez le code dans une fonction, descendez d'un niveau et récupérez la fonction appelante).

Voir http://msdn.Microsoft.com/en-us/library/system.diagnostics.stacktrace(VS.71).aspx

0
Sprotty

J'utilise cette réponse avec un grand effet: Obtenir la propriété, sous forme de chaîne, à partir d'un Expression <Func <TModel, TProperty >>

Je me rends compte que j'ai déjà répondu à cette question il y a un moment. Le seul avantage de mon autre réponse est que cela fonctionne pour les propriétés statiques. Je trouve la syntaxe dans cette réponse beaucoup plus utile car il n’est pas nécessaire de créer une variable du type que vous voulez refléter.

0
hypehuman

J'ai eu quelques difficultés à utiliser les solutions déjà suggérées pour mon cas d'utilisation spécifique, mais j'ai finalement compris. Je ne pense pas que mon cas spécifique mérite une nouvelle question, alors je publie ma solution ici pour référence. (Ceci est très étroitement lié à la question et fournit une solution pour toute autre personne ayant un cas similaire au mien).

Le code que j'ai fini avec ressemble à ceci:

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

L'important pour moi est que dans la classe CompanyControl, le compilateur me permettra uniquement de choisir une propriété booléenne de ICompanyViewModel, ce qui facilitera la tâche des autres développeurs.

La principale différence entre ma solution et la réponse acceptée est que ma classe est générique et que je souhaite uniquement faire correspondre les propriétés du type générique boolean.

0
bikeman868