Se il tuo oggetto era passivo e aveva campi pubblici invece di proprietà, potresti scrivere una procedura più elegante Update()
, accettando il campo come parametro ref
:
static void Update( ref string curVal, string newVal, ref bool isDirty )
{ if( curVal != newVal )
{ isDirty = true;
curVal = newVal;
}
}
//...
updated = false;
Update( ref obj.Prop1, "NewValue1", ref updated );
Update( ref obj.Prop2, "NewValue2", ref updated );
//... updates of many other properties...
if( updated )
{ CommitChanges( obj ); }
Con il sovraccarico di OOP, tuttavia, hai più lavoro da fare. La seguente applicazione console dimostra una possibile soluzione per le proprietà con setter insignificanti (la classe Trivial
) e non banali (la classe NonTrivial
), che eseguono calcoli arbitrari:
// TODO: Maybe store property names as constants?
using System;
using System.Collections.Generic;
class Program {
class Trivial
{ Dictionary<string, string> Props;
public bool IsDirty // TODO: Make it writable for resetting?
{ get; private set; }
public void PropSet( string prop, string value )
{ string currentVal;
currentVal = Props[ prop ];
if( currentVal != value )
{ IsDirty = true;
Props[ prop ] = value;
}
}
public string Prop1
{ set
{ Props["Prop1"] = value; }
get
{ return Props["Prop1"]; }
}
public string Prop2
{ set
{ Props["Prop2"] = value; }
get
{ return Props["Prop2"]; }
}
public Trivial()
{ Props = new Dictionary<string, string>();
Props.Add("Prop1", "");
Props.Add("Prop2", "");
}
}
class NonTrivial
{ delegate void FPropSet( string value );
// TODO: Modify PropInfo according to your needs, e.g. add an
// allowNull flag...
class PropInfo
{ public FPropSet Set;
public string Value;
}
Dictionary< string, PropInfo > Props;
public bool IsDirty // TODO: Make it writable for resetting?
{ get; private set; }
public string Prop1
{ set
{ PropSet( "Prop1", value ); }
get
{ return PropGet( "Prop1" ); }
}
void SetProp1( string value )
{ // Very heavy calculations!
}
public string Prop2
{ set
{ PropSet( "Prop2", value ); }
get
{ return PropGet( "Prop2" ); }
}
void SetProp2( string value )
{ // Super heavy calculations!
}
void RegisterProp( string prop, FPropSet setter, string defValue )
{ PropInfo info = new PropInfo();
info.Set = setter;
info.Value = defValue;
setter( defValue );
Props.Add( prop, info );
}
string PropGet( string prop )
{ return Props[ prop ].Value; }
public void PropSet( string prop, string value )
{ PropInfo info;
info = Props[ prop ];
// TODO: Modify the comparison according to your needs:
if( info.Value != value )
{ info.Set( value );
info.Value = value;
IsDirty = true;
}
}
public NonTrivial()
{ Props = new Dictionary< string, PropInfo >();
RegisterProp( "Prop1", SetProp1, "A" );
RegisterProp( "Prop2", SetProp2, "1" );
}
}
public static void Main(string[] args)
{ Trivial triv = new Trivial ();
NonTrivial nonTriv = new NonTrivial();
triv.Prop1 = "A"; // The setter will not change .IsDirty
triv.Prop2 = "1"; // Should it?
triv.PropSet("Prop1", "B"); // Update property only if changed
if( triv.IsDirty )
{ // This work will not be wasted
}
nonTriv.Prop1 = "A"; // The setter will change .IsDirty
nonTriv.Prop2 = "1"; // Should it?
nonTriv.PropSet("Prop1", "B"); // Update property only if changed
if( nonTriv.IsDirty )
{ // It will not be labor lost
}
Console.ReadKey(true);
}
}
Se al chiamante non interessa sapere se i dati effettivi sono cambiati, puoi annullare la pubblicazione della proprietà IsDirty
e utilizzarla internamente nel tuo metodo CommitChanges()
, che dovrei rinominare in CommitChangesIfAny()
per riflettere la natura condizionale del operazione di commit.
Un'altra opzione è Reflection in cui puoi fare la stessa cosa dinamicamente ( Modifica: vedi il Metodo Smart-Update singolo in Risposta di Dan1701 ).