2011-02-15 8 views
13

Si ma compréhension du fonctionnement interne de cette ligne est correcte:C# getter et setter sténographie

public int MyInt { get; set; } 

Ensuite, dans les coulisses ce que cela:

private int _MyInt { get; set; } 
Public int MyInt { 
    get{return _MyInt;} 
    set{_MyInt = value;} 
} 

Ce que je vraiment besoin est:

private bool IsDirty { get; set; } 

private int _MyInt { get; set; } 
Public int MyInt { 
    get{return _MyInt;} 
    set{_MyInt = value; IsDirty = true;} 
} 

Mais je voudrais l'écrire quelque chose comme:

private bool IsDirty { get; set; } 

public int MyInt { get; set{this = value; IsDirty = true;} } 

Ce qui ne fonctionne pas. La chose est quelques-uns des objets dont j'ai besoin pour faire l'IsDirty sur des douzaines de propriétés et j'espère qu'il y a un moyen d'utiliser le getter/setter automatique tout en définissant IsDirty quand le champ est modifié. Est-ce possible ou dois-je me résigner à tripler la quantité de code dans mes classes?

+0

Go avec IronPython à la place. C'est sur le CLR, mais prend en charge la programmation orientée aspect, ce dont vous avez besoin. :) – bzlm

+0

Si vous utilisez Visual Studio, vous pouvez créer un fragment de code pour vous aider à gagner du temps au moins. – ebrown

Répondre

21

Vous devrez gérer vous-même:

private bool IsDirty { get; set; } 

private int _myInt; // Doesn't need to be a property 
Public int MyInt { 
    get{return _myInt;} 
    set{_myInt = value; IsDirty = true;} 
} 

Il n'y a pas de syntaxe disponible qui ajoute une logique personnalisée à un setter tout en utilisant le mécanisme automatique de propriété. Vous aurez besoin d'écrire ceci avec votre propre champ de support.

Ceci est un problème courant - par exemple lors de l'implémentation de INotifyPropertyChanged.

+0

C'est exactement ce qu'il essaie d'éviter :) –

+1

@Nathan: Oui - mais c'est vraiment ce qu'il va devoir faire - les auto-accessoires ne fonctionnent pas avec la logique personnalisée. Cela étant dit, ma mise en œuvre est différente de celle de l'OP - j'utilise un champ à la place d'un auto-prop comme backing field. –

+0

Bleh, bien alors. Merci pour la note de terrain. – William

11

Créez un décorateur IsDirty (motif de conception) pour envelopper vos propriétés nécessitant la fonctionnalité d'indicateur isDirty.

public class IsDirtyDecorator<T> 
{ 
    public bool IsDirty { get; private set; } 

    private T _myValue; 
    public T Value 
    { 
     get { return _myValue; } 
     set { _myValue = value; IsDirty = true; } 
    } 
} 

public class MyClass 
{ 
    private IsDirtyDecorator<int> MyInt = new IsDirtyDecorator<int>(); 
    private IsDirtyDecorator<string> MyString = new IsDirtyDecorator<string>(); 

    public MyClass() 
    { 
     MyInt.Value = 123; 
     MyString.Value = "Hello"; 
     Console.WriteLine(MyInt.Value); 
     Console.WriteLine(MyInt.IsDirty); 
     Console.WriteLine(MyString.Value); 
     Console.WriteLine(MyString.IsDirty); 
    } 
} 
+0

Intéressant. Question cependant, mes objets ont beaucoup de propriétés et je ne veux vraiment qu'un IsDirty. Lorsque l'une de mes propriétés est mise à jour, je veux que celui qui est mis à jour pour définir IsDirty = true. Cela fonctionne-t-il toujours avec votre code? – William

+2

Cela ne fonctionnera que pour une propriété à la fois. Chaque propriété possède son propre indicateur IsDirty. Si vous voulez un seul indicateur IsDirty pour toutes les propriétés, si vous voulez qu'il fonctionne pour chaque propriété, vous devrez modifier le Decortor pour qu'il contienne une référence à un indicateur IsDirty. –

1

J'ai créé une classe personnalisée Property<T> pour effectuer des opérations courantes de ce type. Je ne l'ai pas encore utilisé à fond, mais il pourrait être utilisé dans ce scénario.

code se trouve ici: http://pastebin.com/RWTWNNCU

Vous pouvez l'utiliser comme suit:

readonly Property<int> _myInt = new Property<int>(); 
public int MyInt 
{ 
    get { return _myInt.GetValue(); } 
    set { _myInt.SetValue(value, SetterCallbackOption.OnNewValue, SetDirty); } 
} 

private void SetDirty(int oldValue, int newValue) 
{ 
    IsDirty = true; 
} 

Les poignées classe de propriété que d'appeler le délégué passé quand une nouvelle valeur est passée grâce au paramètre SetterCallbackOption. Ceci est par défaut, donc il peut être abandonné.

MISE À JOUR:

Cela ne fonctionnera pas apparemment lorsque vous devez prendre en charge plusieurs types (en plus int), parce que le délégué ne correspond pas à l'époque. Vous pouvez toujours ajuster le code en fonction de vos besoins.

2

Vous pouvez le rendre simple ou complexe. Cela dépend de combien de travail vous voulez investir. Vous pouvez utiliser la programmation orientée aspect pour ajouter l'aspect via un tisserand IL dans le code IL avec, par exemple, PostSharp. Ou vous pouvez créer une classe simple qui gère l'état de votre propriété. C'est tellement simple que la première approche ne rapporte que si vous avez vraiment beaucoup de propriétés à gérer de cette façon.

using System; 

class Dirty<T> 
{ 
    T _Value; 
    bool _IsDirty; 

    public T Value 
    { 
     get { return _Value; } 
     set 
     { 
      _IsDirty = true; 
      _Value = value; 
     } 
    } 

    public bool IsDirty 
    { 
     get { return _IsDirty; } 
    } 

    public Dirty(T initValue) 
    { 
     _Value = initValue; 
    } 
} 

class Program 
{ 
    static Dirty<int> _Integer; 
    static int Integer 
    { 
     get { return _Integer.Value; } 
     set { _Integer.Value = value; } 
    } 

    static void Main(string[] args) 
    { 
     _Integer = new Dirty<int>(10); 
     Console.WriteLine("Dirty: {0}, value: {1}", _Integer.IsDirty, Integer); 
     Integer = 15; 
     Console.WriteLine("Dirty: {0}, value: {1}", _Integer.IsDirty, Integer); 
    } 
} 

Une autre possibilité consiste à utiliser une classe proxy qui est générée lors de l'exécution et qui ajoute l'aspect pour vous. Avec .NET 4, il y a une classe qui gère déjà cet aspect pour vous. Il est appelé ExpandObject qui vous avertit via un événement lorsqu'une propriété est modifiée. La bonne chose est que ExpandoObject vous permet de définir à l'exécution n'importe quelle quantité de propriétés et vous recevez des notifications sur chaque changement de propriété. La liaison de données avec WPF est très facile avec ce type.

dynamic _DynInteger = new ExpandoObject(); 

_DynInteger.Integer = 10; 
((INotifyPropertyChanged)_DynInteger).PropertyChanged += (o, e) => 
{ 
    Console.WriteLine("Property {0} changed", e.PropertyName); 
}; 

Console.WriteLine("value: {0}", _DynInteger.Integer); 
_DynInteger.Integer = 20; 
Console.WriteLine("value: {0}", _DynInteger.Integer); 

vôtre, Alois Kraus

2

Je vais ajouter à la réponse de Simon Hughes. Je propose la même chose, mais ajoute un moyen d'autoriser la classe décorator à mettre à jour automatiquement un indicateur IsDirty global. Vous pouvez trouver qu'il est moins complexe de le faire à l'ancienne, mais cela dépend du nombre de propriétés que vous exposez et du nombre de classes qui nécessiteront la même fonctionnalité.

public class IsDirtyDecorator<T> 
{ 
    private T _myValue; 
    private Action<bool> _changedAction; 

    public IsDirtyDecorator<T>(Action<bool> changedAction = null) 
    { 
     _changedAction = changedAction; 
    } 

    public bool IsDirty { get; private set; } 

    public T Value 
    { 
     get { return _myValue; } 
     set 
     { 
      _myValue = value; 
      IsDirty = true; 
      if(_changedAction != null) 
       _changedAction(IsDirty); 
     } 
    } 
} 

Maintenant vous pouvez avoir votre classe de décorateur à jour automatiquement une autre propriété IsDirty dans une autre classe:

class MyObject 
{ 
    private IsDirtyDecorator<int> _myInt = new IsDirtyDecorator<int>(onValueChanged); 
    private IsDirtyDecorator<int> _myOtherInt = new IsDirtyDecorator<int>(onValueChanged); 

    public bool IsDirty { get; private set; } 

    public int MyInt 
    { 
     get { return _myInt.Value; } 
     set { _myInt.Value = value; } 
    } 

    public int MyOtherInt 
    { 
     get { return _myOtherInt.Value; } 
     set { _myOtherInt.Value = value; } 
    } 

    private void onValueChanged(bool dirty) 
    { 
     IsDirty = true; 
    } 

}