2016-07-20 4 views
1

J'ai une classe comme suit:Comment obtenir le champ de support Getter à partir de PropertyInfo?

class Foo : PropertyChangedBase { 
    private int _property; 

    public int Property { 
     get { return _property; } 
     set { OnAssignPropertyChanged("Property",() => _property, value); } 
} 

PropertyChangedBase implémente INotifyPropertyChanged avec les méthodes suivantes:

protected void OnAssignmentPropertyChanged<T>(string propertyName, Expression<Func<T>> fieldExpression, T value) 
    { 
     var get = fieldExpression.Compile(); 
     if (get().Equals(value)) 
     { 
      return; 
     } 

     // invoke set property method 
     SetProperty(fieldExpression, value); 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    private void SetProperty<T>(Expression<Func<T>> fieldExpression, T value) 
    { 
     if (fieldExpression == null) 
     { 
      throw new ArgumentNullException(nameof(fieldExpression)); 
     } 

     var memberExpression = fieldExpression.Body as MemberExpression; 
     if (memberExpression == null) 
     { 
      throw new ArgumentException("fieldExpression"); 
     } 

     var field = memberExpression.Member as FieldInfo; 
     if (field == null) 
     { 
      throw new ArgumentException("fieldExpression"); 
     } 

     field.SetValue(this, value); 
    } 

Je préférerais appeler:

OnAssignPropertyChanged(() => Property, value); 

La seule façon dont cela fonctionne est si je peux obtenir le champ de sauvegarde pour le getter de propriété, puis passer à SetProperty. Est-il possible d'obtenir le FieldInfo ou un membre de la propriété get méthode?

Répondre

1

En général, oui, vous pouvez le faire, au moins dans des conditions contrôlées. Mais le seul cas que vous devriez faire est quand vous êtes absolument sûr de ce que vous faites et seulement avec un soutien limité, car il y aura des cas que vous ne pouvez pas gérer.

Jetez un oeil à la réponse ici: Find all property references using reflection. La cible est un peu différente mais l'approche est similaire pour trouver des références de champs. Étant donné que la réponse contient déjà le code nécessaire, je vais simplement indiquer le chemin à suivre:

Tous les éléments de métadonnées dans .Net sont référencés par des jetons. Pour obtenir des jetons utilisés à l'intérieur d'une méthode, vous devez analyser le MethodBody (en ignorant toutes les choses que vous ne voulez pas inspecter), puis resolve the found tokens in their module. N'oubliez pas d'utiliser le BitConverter lors de la lecture des jetons du flux pour les résoudre.

Mais maintenant à l'envers; La seule fois où vous pouvez vraiment l'utiliser en toute sécurité pour trouver les champs de sauvegarde d'un getter de propriétés, c'est quand vous trouvez une méthode get simple, avec une séquence d'opcode bien définie comme Ldfld, Ret ou quelque chose comme ça. Peut-être que vous pouvez définir quelques modèles que le compilateur C# émettra pour les propriétés simples et autoimplémentées. Si vous trouvez quelque chose de différent, il n'y a pas d'autre moyen de démissionner et de lancer une exception, car le getter peut contenir n'importe quel code. Comme toujours avec la réflexion, n'utilisez que des approches de liste blanche, vérifiez les conditions que vous attendez et lancez des exceptions dans tout autre cas ou vous rencontrerez une exception NullReferenceException tôt ou tard.

Si cela vaut la peine, c'est à vous de décider, mais en général vous pouvez le faire depuis .Net 2.0 et n'avez même pas besoin d'une librairie externe.

+0

Cela semble raisonnable. Le cas d'utilisation est très spécifique pour les classes qui héritent de la classe de base. Basé sur notre guide de codage, nous pouvons appliquer les règles d'utilisation. Merci. – IAbstract

1

Non, dans le cas général ne peut pas. Il suffit de comparer deux classes:

public class Test { 
    private int _propA; 
    private int _propB; 

    public int PropA {get { return _propA; }} 
    public int PropB {get { return _propB; }} 
} 

public class TestSwapped { 
    private int _propA; 
    private int _propB; 

    // please, notice swapped backing fields 
    public int PropA {get { return _propB; }} 
    public int PropB {get { return _propA; }} 
} 

vous obtiendrez des tableaux identiques PropertyInfo[] et FieldInfo[] mais différents champs d'accompagnement

+0

Vous devrez bien sûr comparer les corps des accesseurs. C'est difficile, comme n'importe quel code IL pourrait être là. J'ai construit en mode Release votre exemple et essayé 'typeof (TestSwapped) .GetProperty (" PropA "). GetMethod.GetMethodBody(). GetILAsByteArray()' et similaire pour 'Test'. Il charge simplement "this" sur la pile, charge le champ correspondant ('ldfld') et le renvoie. La différence a été vue dans l'argument de l'instruction 'ldfld'. –

+0

@Jeppe Stig Nielsen: havig * IL code *, bien sûr, nous pouvons trouver (ou au moins proposer une supposition raisonnable) le champ de support dans les cas simples (c'est-à-dire 'return _propB;'); dans * cas général *, cependant, c'est * impossible * (modification du théorème d'Alan Turing). Par exemple. get 'get {if (_propA> 0) retourne _propB; else return _propB + 1;} 'premier champ chargé (dépend du compilateur) est' _propA' backing field est '_propB' –

+0

@DmitryBychenko: excellent point sur les propriétés. Mon implémentation est spécifique aux getters simples: 'get {return _field; } '... Les modèles ou ViewModels implémentant notre notification de changement de propriété doivent être des getters simples. – IAbstract

0

Vous ne pouvez pas. La propriété peut ne pas avoir de champs de sauvegarde ou de groupes de zones de sauvegarde. Même la propriété set ne peut contenir aucun champ de sauvegarde.

public Int32 Prop 
{ 
set { Debug.WriteLine(value.ToString()); } 
get { return 1; } 
} 

Qu'attendez-vous de FieldInfo?

+0

Dans les propriétés avec auto-accesseurs, vous pouvez obtenir les champs de retour automatique par réflexion: par ex. {get; ensemble; } a des champs de sauvegarde automatique. Donc, je suppose que vous pourriez obtenir le champ de sauvegarde si, comme dans mon exemple, le getter retourne effectivement le champ; – IAbstract

+0

Eh bien, vous ne pouvez pas aussi loin que je sache. –

+0

C'est possible, mais c'est un énorme travail pour le compilateur. Bien que les cas simples soient simples, les cas peuvent facilement devenir compliqués. –

0

La propriété est juste une syntaxe de sucre pour une paire de méthodes set/get, ou des mutateurs. Être une méthode leur permet de contenir autant de code que nécessaire, y compris d'être simplement vide et, bien sûr, il n'y a pas besoin d'avoir un champ de sauvegarde de la perspective du compilateur.