2011-02-15 17 views
13

Si mi comprensión del funcionamiento interno de esta línea es correcta:C# get y set taquigrafía

public int MyInt { get; set; } 

Entonces detrás de las escenas hace esto:

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

Lo que realmente necesito es:

private bool IsDirty { get; set; } 

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

Pero me gustaría escribir algo como:

private bool IsDirty { get; set; } 

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

Lo que no funciona. Lo que pasa es que algunos de los objetos que necesito para hacer el IsDirty tienen docenas de propiedades y espero que haya una forma de usar el auto getter/setter pero aun así establecer IsDirty cuando el campo es modificado.

¿Es esto posible o solo tengo que resignarme a triplicar la cantidad de código en mis clases?

+0

ir con IronPython lugar. Está en el CLR, pero admite programación orientada a aspectos, que es lo que necesita. :) – bzlm

+0

Si está usando Visual Studio, es posible que pueda crear un fragmento de código para ayudarlo a ahorrar tiempo, al menos. – ebrown

Respuesta

21

Tendrá que manejar esto por sí mismo:

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

No hay sintaxis disponibles que se suma lógica personalizada para un regulador sin dejar de utilizar el mecanismo de la propiedad automático. Deberá escribir esto con su propio campo de respaldo.

Esto es un problema común, por ejemplo, al implementar INotifyPropertyChanged.

+0

Eso es exactamente lo que está tratando de evitar :) –

+1

@Nathan: Sí, pero eso es realmente lo que tendrá que hacer: los accesorios automáticos no funcionan con una lógica personalizada. Una vez dicho esto, mi implementación es diferente a la de OP: estoy usando un campo en lugar de un autopropulsado como campo de respaldo. –

+0

Bleh, bien entonces. Gracias por la nota de campo. – William

11

Cree un decorador IsDirty (patrón de diseño) para envolver algunas de sus propiedades que requieren la funcionalidad de indicador 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

Interesante. Sin embargo, la pregunta es que mis objetos tienen muchas propiedades y realmente solo quiero un IsDirty. Cuando alguna de mis propiedades se actualiza, quiero que la que se actualice configure IsDirty = true. ¿Eso todavía funciona con tu código? – William

+2

Esto solo funcionará para una propiedad a la vez. Cada propiedad tiene su propia bandera IsDirty. Si desea un solo indicador IsDirty para todas las propiedades, si desea que funcione para cada propiedad, tendrá que modificar Decortor para mantener una referencia a un indicador IsDirty. –

1

He creado una clase personalizada Property<T> para hacer operaciones comunes como esa. Sin embargo, todavía no lo he usado a fondo, pero podría usarse en este escenario.

Código se puede encontrar aquí: http://pastebin.com/RWTWNNCU

se podría utilizar de la siguiente manera:

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

clase La propiedad sólo se ocupa de llamar al delegado pasado, cuando un nuevo valor se pasa gracias al parámetro SetterCallbackOption. Esto es predeterminado, por lo que se puede descartar.

ACTUALIZACIÓN:

Esto no funcionará al parecer, cuando se necesita para soportar múltiples tipos (además int), debido a que el delegado no coincidir entonces. Por supuesto, siempre puede ajustar el código para satisfacer sus necesidades.

2

Puede hacerlo simple o complejo. Depende de la cantidad de trabajo que desee invertir. Puede usar la programación orientada a aspectos para agregar el aspecto a través de un tejedor IL al código IL con p. Ej. PostSharp. O puede crear una clase simple que maneje el estado de su propiedad. Es tan simple que el primer enfoque solo vale la pena si tiene muchas propiedades para manejar de esta manera.

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

Otra posibilidad es utilizar una clase de proxy que se genera en el tiempo de ejecución que le agrega el aspecto. Con .NET 4 hay una clase que ya maneja este aspecto para usted. Se llama ExpandObject que le notifica a través de un evento cuando cambia una propiedad. Lo bueno es que ExpandoObject le permite definir en tiempo de ejecución cualquier cantidad de propiedades y recibe notificaciones sobre cada cambio de una propiedad. La vinculación de datos con WPF es muy fácil con este tipo.

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

Suyo, Alois Kraus

2

voy a añadir a la respuesta Simon Hughes. Propongo lo mismo, pero agregue una forma de permitir que la clase de decorador actualice una bandera IsDirty global automáticamente. Puede que resulte menos complejo hacerlo a la antigua, pero depende de cuántas propiedades exponga y cuántas clases requerirán la misma funcionalidad.

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

Ahora usted puede tener su clase decoradora actualiza automáticamente alguna otra propiedad IsDirty en otra clase:

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

} 
Cuestiones relacionadas