28

以下は、オブジェクトの状態を定義する列挙型と、この列挙型の実装を示すクラスの簡単な例です。

public enum StatusEnum
{
    Clean = 0,
    Dirty = 1,
    New = 2,
    Deleted = 3,
    Purged = 4
}


public class Example_Class
{
    private StatusEnum _Status = StatusEnum.New;

    private long _ID;
    private string _Name;

    public StatusEnum Status
    {
        get { return _Status; }
        set { _Status = value; }
    }

    public long ID
    {
        get { return _ID; }
        set { _ID = value; }
    }

    public string Name
    {
        get { return _Name; }
        set { _Name = value; }
    }
}

クラス オブジェクトにデータベースからのデータを入力するとき、enum 値を「clean」に設定します。ほとんどのロジックをプレゼンテーション層から除外することを目標に、プロパティが変更されたときに列挙値を「ダーティ」に設定するにはどうすればよいでしょうか。

私は次のようなことを考えていました。

public string Name
{
    get { return _Name; }
    set 
    {
        if (value != _Name)
        {
               _Name = value; 
           _Status = StatusEnum.Dirty;
        }
    }   
}

クラスの各プロパティのセッターで。

これは良いアイデアのように聞こえますか?プレゼンテーション層でそれを行わずにダーティ フラグを割り当てる方法について、より良いアイデアを持っている人はいますか?

4

11 に答える 11

17

この方法で実装し、コードの量を減らしたい場合は、アスペクト指向プログラミングの適用を検討してください。

たとえば、 PostSharpのようなコンパイル時ウィーバーを使用して、プロパティに適用できる「アスペクト」を作成できます。この側面により、適切な場合にダーティフラグが設定されていることを確認します。

アスペクトは次のようになります。

[Serializable]
[AttributeUsage(AttributeTargets.Property)]
public class ChangeTrackingAttribute : OnMethodInvocationAspect
{
    public override void OnInvocation( MethodInvocationEventArgs e )
    {
        if( e.Delegate.Method.ReturnParameter.ParameterType == typeof(void) )
        {
              // we're in the setter
              IChangeTrackable target = e.Delegate.Target as IChangeTrackable;

              // Implement some logic to retrieve the current value of 
              // the property
              if( currentValue != e.GetArgumentArray()[0] )
              {
                  target.Status = Status.Dirty;
              }
              base.OnInvocation (e);
        } 
    }  
} 

オフコース、これは、ChangeTrackingを実装するクラスIChangeTrackableが、少なくとも'Status'プロパティを持つインターフェイス(カスタムインターフェイス)を実装する必要があることを意味します。

カスタム属性を作成することもできます。また、上記で作成したアスペクトが、この属性ChangeTrackingPropertyで装飾されたプロパティにのみ適用されるようにすることもできます。ChangeTrackingProperty

例えば:

public class Customer : IChangeTrackable
{
    public DirtyState Status
    {
        get; set;
    }

    [ChangeTrackingProperty]
    public string Name
    { get; set; }
}

これは私がそれを見る方法です。PostSharpがコンパイル時に、ChangeTrackingProperty属性で装飾されたプロパティを持つクラスがIChangeTrackableインターフェイスを実装しているかどうかを確認することもできます。

于 2009-04-30T07:15:37.813 に答える
1

「あなたの型を不変にすることを検討してください」というアドバイスとは別に、ここに私が書いたものがあります(途中でJonとMarcに何かを教えてもらいました)

public class Example_Class
{    // snip
     // all properties are public get and private set

     private Dictionary<string, Delegate> m_PropertySetterMap;

     public Example_Class()
     {
        m_PropertySetterMap = new Dictionary<string, Delegate>();
        InitializeSettableProperties();
     }
     public Example_Class(long id, string name):this()
     {   this.ID = id;    this.Name = name;   }

     private void InitializeSettableProperties()
     {
        AddToPropertyMap<long>("ID",  value => { this.ID = value; });
        AddToPropertyMap<string>("Name", value => { this.Name = value; }); 
     }
     // jump thru a hoop because it won't let me cast an anonymous method to an Action<T>/Delegate
     private void AddToPropertyMap<T>(string sPropertyName, Action<T> setterAction)
     {   m_PropertySetterMap.Add(sPropertyName, setterAction);            }

     public void SetProperty<T>(string propertyName, T value)
     {
        (m_PropertySetterMap[propertyName] as Action<T>).Invoke(value);
        this.Status = StatusEnum.Dirty;
     }
  }

アイデアが得られます..可能な改善: PropertyNames に定数を使用し、プロパティが実際に変更されたかどうかを確認します。ここでの1つの欠点は、

obj.SetProperty("ID", 700);         // will blow up int instead of long
obj.SetProperty<long>("ID", 700);   // be explicit or use 700L
于 2009-04-30T15:36:04.397 に答える