web-dev-qa-db-ja.com

C#ゲッターとセッターの省略形

この行の内部動作についての私の理解が正しい場合:

public int MyInt { get; set; }

次に、舞台裏でこれを行います。

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

私が本当に必要なのは:

private bool IsDirty { get; set; }

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

しかし、私はそれを次のように書きたいと思います:

private bool IsDirty { get; set; }

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

これは機能しません。問題は、IsDirtyを実行するために必要なオブジェクトのいくつかに数十のプロパティがあり、自動ゲッター/セッターを使用する方法があることを望んでいますが、フィールドが変更されてもIsDirtyを設定します。

これは可能ですか、それともクラスのコード量を3倍にするために辞任する必要がありますか?

14
William

これは自分で処理する必要があります。

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

自動プロパティメカニズムを使用しながら、セッターにカスタムロジックを追加する構文はありません。あなたはあなた自身のバッキングフィールドでこれを書く必要があるでしょう。

これは一般的な問題です。たとえば、INotifyPropertyChangedを実装する場合です。

24
Reed Copsey

IsDirtyフラグ機能を必要とするいくつかのプロパティをラップする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);
    }
}
11
Simon Hughes

サイモン・ヒューズの答えに追加します。同じことを提案しますが、デコレータクラスがグローバルIsDirtyフラグを自動的に更新できるようにする方法を追加します。昔ながらの方法で行うのはそれほど複雑ではないかもしれませんが、公開するプロパティの数と、同じ機能を必要とするクラスの数によって異なります。

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

これで、デコレータクラスに別のクラスの他のIsDirtyプロパティを自動的に更新させることができます。

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

}
2
Phil

単純または複雑にすることができます。それはあなたがどれだけの仕事を投資したいかによります。アスペクト指向プログラミングを使用して、ILウィーバーを介してアスペクトをILコードに追加できます。 PostSharp 。または、プロパティの状態を処理する単純なクラスを作成することもできます。非常に単純なので、前者のアプローチは、この方法で処理するプロパティが本当に多い場合にのみ効果があります。

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

もう1つの可能性は、実行時に生成されるプロキシクラスを使用することです。これにより、アスペクトが追加されます。 .NET 4には、この側面をすでに処理するクラスがあります。これは ExpandObject と呼ばれ、プロパティが変更されたときにイベントを介して通知します。素晴らしい点は、ExpandoObjectを使用すると、実行時に任意の数のプロパティを定義でき、プロパティのすべての変更に関する通知を受け取ることができることです。このタイプでは、WPFを使用したデータバインディングは非常に簡単です。

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

あなたの、アロイス・クラウス

2
Alois Kraus

そのような一般的な操作を行うために、カスタムProperty<T>クラスを作成しました。まだ完全には使用していませんが、このシナリオでは使用できます。

コードはここにあります: http://Pastebin.com/RWTWNNC

次のように使用できます。

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

Propertyクラスは、SetterCallbackOptionパラメーターのおかげで新しい値が渡されたときに、渡されたデリゲートの呼び出しのみを処理します。これはデフォルトなので、削除できます。

更新:

複数のタイプ(int以外)をサポートする必要がある場合、デリゲートが一致しないため、これは明らかに機能しません。もちろん、ニーズに合わせてコードをいつでも調整できます。

1
Steven Jeuris