2009-07-30 10 views
35

Je voudrais obtenir le nom de la propriété quand je suis dedans par le mécanisme de réflexion. C'est possible?Comment obtenir le nom de propriété actuel par réflexion?

Mise à jour: J'ai le code comme ceci:

public CarType Car 
    { 
     get { return (Wheel) this["Wheel"];} 
     set { this["Wheel"] = value; } 
    } 

Et parce que je besoin de plus de propriétés comme cela, je voudrais faire quelque chose comme ceci:

public CarType Car 
    { 
     get { return (Wheel) this[GetThisPropertyName()];} 
     set { this[GetThisPropertyName()] = value; } 
    } 
+1

+1 Je me suis également demandé à ce sujet lors de l'implémentation System.Configuration.ApplicationSettingsBase –

Répondre

47

Puisque les propriétés sont en fait que des méthodes que vous pouvez faire et nettoyer le get_ retourné:

class Program 
    { 
     static void Main(string[] args) 
     { 
      Program p = new Program(); 
      var x = p.Something; 
      Console.ReadLine(); 
     } 

     public string Something 
     { 
      get 
      { 
       return MethodBase.GetCurrentMethod().Name; 
      } 
     } 
    } 

Si vous profiler les performances que vous devriez trouver MethodBase.GetCurrentMethod() est plus rapide que miles StackFrame. Dans .NET 1.1 vous aurez aussi des problèmes avec StackFrame en mode release (de mémoire je pense que j'ai trouvé que c'était 3x plus rapide). Cela dit, je suis sûr que le problème de performance ne causera pas trop de problèmes, bien qu'une discussion intéressante sur la lenteur de StackFrame puisse être found here.

Je suppose qu'une autre option si vous étiez préoccupé par les performances serait de créer un extrait de code Visual Studio Intellisense qui crée la propriété pour vous et crée également une chaîne qui correspond au nom de la propriété.

+0

Intéressant - Je n'étais pas au courant de GetCurrentMethod() – BlueMonkMN

+0

Est-ce que quelque chose de semblable à ceci pour 'Properties'? –

0

Oui, il est!

string test = "test string"; 
Type type = test.GetType(); 

PropertyInfo[] propInfos = type.GetProperties(); 
for (int i = 0; i < propInfos.Length; i++) 
{ 
    PropertyInfo pi = (PropertyInfo)propInfos.GetValue(i); 
    string propName = pi.Name; 
} 
0

Essayez d'utiliser System.Diagnostics.StackTrace à réfléchir sur la pile d'appels. La propriété doit se trouver quelque part dans la pile d'appels (probablement en haut si vous l'appelez directement à partir du code de la propriété).

5

Utilisez MethodBase.GetCurrentMethod() à la place! La réflexion est utilisée pour travailler avec des types qui ne peuvent pas être faits à la compilation.

Obtenir le nom de l'accesseur de propriété dans lequel vous vous trouvez peut être décidé au moment de la compilation, donc vous ne devriez probablement pas utiliser la réflexion pour cela.

Vous obtenez cependant le nom de la méthode d'accès de la pile d'appels en utilisant System.Diagnostics.StackTrace.

string GetPropertyName() 
    { 
     StackTrace callStackTrace = new StackTrace(); 
     StackFrame propertyFrame = callStackTrace.GetFrame(1); // 1: below GetPropertyName frame 
     string properyAccessorName = propertyFrame.GetMethod().Name; 

     return properyAccessorName.Replace("get_","").Replace("set_",""); 
    } 
+1

je fais des choses comme ça et ça m'a pris de nouveau. Attention, cela peut causer des problèmes si la propriété est inline: mettre [MethodImpl (MethodImplOptions.NoInlining)] avant que le getter/setter ne puisse l'aider, mais cela n'est pas pratique ... –

6

Je voudrais en savoir plus sur le contexte dans lequel vous en avez besoin, car il me semble que vous devriez déjà savoir ce que la propriété que vous travaillez avec la accesseur propriété. Si vous devez, cependant, vous pouvez probablement utiliser MethodBase.GetCurrentMethod() .Name et supprimer n'importe quoi après get_/set_.

Mise à jour:

En fonction de vos changements, je dirais que vous devriez utiliser l'héritage plutôt que la réflexion. Je ne sais pas quelles données sont dans votre dictionnaire, mais il me semble que vous voulez vraiment avoir différentes classes de voitures, disons Sedan, Roadster, Buggy, StationWagon, ne pas garder le type dans une variable locale. Ensuite, vous auriez des implémentations de méthodes qui font la bonne chose pour ce type de voiture. Au lieu de chercher le type de voiture que vous possédez, puis de faire quelque chose, vous appelez simplement la méthode appropriée et l'objet Car fait ce qu'il faut en fonction de son type.

public interface ICar 
{ 
     void Drive(decimal velocity, Orientation orientation); 
     void Shift(int gear); 
     ... 
} 

public abstract class Car : ICar 
{ 
     public virtual void Drive(decimal velocity, Orientation orientation) 
     { 
      ...some default implementation... 
     } 

     public abstract void Shift(int gear); 

     ... 
} 

public class AutomaticTransmission : Car 
{ 
     public override void Shift(int gear) 
     { 
      ...some specific implementation... 
     } 
} 

public class ManualTransmission : Car 
{ 
     public override void Shift(int gear) 
     { 
      ...some specific implementation... 
     } 
} 
+0

Plus d'exemples sur: http://www.codeproject.com /KB/dotnet/MethodName.aspx –

+0

J'ai ajouté quelques explications supplémentaires sur mon contexte. –

+0

Vos mises à jour montrent juste pourquoi vous ne devriez pas essayer d'utiliser la réflexion pour cela. En utilisant la méthode de réflexion suggérée, vous finirez probablement par découvrir que vous êtes dans "GetThisPropertyName", et vous déciderez de la propriété "ThisPropertyName". Vous devrez modifier l'exemple pour faire un appel de pile vers le bas, afin d'obtenir l'appel de propriété réel. Bien que ce soit possible, cela ressemble à une surpuissance. Votre code sera saupoudré de copier/coller de ces appels [GetThisPropertyName()], et il ne sera plus lisible (IMHO). Je m'en tiens à l'utilisation de l'accesseur de dictée de corde normale, personnellement. –

3

FWIW je mis en place un système comme celui-ci:

[CrmAttribute("firstname")] 
    public string FirstName 
    { 
     get { return GetPropValue<string>(MethodBase.GetCurrentMethod().Name); } 
     set { SetPropValue(MethodBase.GetCurrentMethod().Name, value); } 
    } 

    // this is in a base class, skipped that bit for clairty 
    public T GetPropValue<T>(string propName) 
    { 
     propName = propName.Replace("get_", "").Replace("set_", ""); 
     string attributeName = GetCrmAttributeName(propName); 
     return GetAttributeValue<T>(attributeName);    
    } 

    public void SetPropValue(string propName, object value) 
    { 
     propName = propName.Replace("get_", "").Replace("set_", ""); 
     string attributeName = GetCrmAttributeName(propName); 
     SetAttributeValue(attributeName, value); 
    } 

    private static Dictionary<string, string> PropToAttributeMap = new Dictionary<string, string>(); 
    private string GetCrmAttributeName(string propertyName) 
    { 
     // keyName for our propertyName to (static) CrmAttributeName cache 
     string keyName = this.GetType().Name + propertyName; 
     // have we already done this mapping? 
     if (!PropToAttributeMap.ContainsKey(keyName)) 
     { 
      Type t = this.GetType(); 
      PropertyInfo info = t.GetProperty(propertyName); 
      if (info == null) 
      { 
       throw new Exception("Cannot find a propety called " + propertyName); 
      } 

      object[] attrs = info.GetCustomAttributes(false); 
      foreach (object o in attrs) 
      { 
       CrmAttributeAttribute attr = o as CrmAttributeAttribute ; 
       if (attr != null) 
       { 
        // found it. Save the mapping for next time. 
        PropToAttributeMap[keyName] = attr.AttributeName; 
        return attr.AttributeName; 
       } 
       } 
       throw new Exception("Missing MemberOf attribute for " + info.Name + "." + propertyName + ". Could not auto-access value"); 
      } 

      // return the existing mapping 
      string result = PropToAttributeMap[keyName]; 
      return result; 
     } 

Il y a aussi une classe personnalisée attribut appelé CrmAttributeAttribute.

Je recommande fortement contre en utilisant GetStackFrame() dans le cadre de votre solution, ma version originale de la solution était à l'origine de la beaucoup plus propres:

return GetPropValue<string>(); 

Mais il était 600x plus lent que la version ci-dessus .

3

Exemple légèrement confus que vous avez présenté, sauf si je ne comprends pas. A partir de C# 6.0, vous pouvez utiliser l'opérateur nameof.

public CarType MyProperty 
{ 
    get { return (CarType)this[nameof(MyProperty)]}; 
    set { this[nameof(MyProperty)] = value]}; 
} 

Si vous avez une méthode qui gère votre getter/setter de toute façon, vous pouvez utiliser l'attribut 4.5 C# CallerMemberName, dans ce cas, vous ne même pas besoin de répéter le nom.

public CarType MyProperty 
{ 
    get { return Get<CarType>(); } 
    set { Set(value); } 
} 

public T Get<T>([CallerMemberName]string name = null) 
{ 
    return (T)this[name]; 
} 

public void Set<T>(T value, [CallerMemberName]string name = null) 
{ 
    this[name] = value; 
} 
+0

Je n'arrête pas d'oublier la magie de 'nameof' à chaque fois. c'est beaucoup plus facile de simplement appeler le 'nameof (propriété)' autre que toutes les autres solutions que j'ai vues. confirmant que le simple fait de 'nameof (item.PropertyName)' a fonctionné comme un charme pour moi. bien que cela puisse différer pour d'autres selon leur situation. – Niklas

+1

@Niklas 'nameof' est l'une des plus grandes améliorations de syntaxe jamais introduites dans C#. Rend l'utilisation des chaînes littérales dans le code presque obsolète, améliorant ainsi non seulement la commodité mais aussi la sécurité du code. Je l'utilise tout le temps. –

Questions connexes