C# getter and setter стенография
Если мое понимание внутренней работы этой линии верно:
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 при изменении поля.
Возможно ли это, или мне просто нужно смириться с увеличением количества кода в моих классах в три раза?
5 ответов
Вам нужно будет справиться с этим самостоятельно:
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
,
Создайте декоратор 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);
}
}
Я собираюсь добавить к ответу Саймона Хьюза. Я предлагаю то же самое, но добавляю способ, позволяющий классу декоратора автоматически обновлять глобальный флаг 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;
}
}
Вы можете сделать это простым или сложным. Это зависит от того, сколько работы вы хотите инвестировать. Вы можете использовать аспектно-ориентированное программирование для добавления аспекта через 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);
}
}
Другая возможность состоит в том, чтобы использовать прокси-класс, который генерируется во время выполнения и добавляет этот аспект для вас. В.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);
С уважением, Алоис Краус
Я создал кастом Property<T>
класс для выполнения общих операций, подобных этому. Хотя я еще не использовал его полностью, но он мог бы быть использован в этом сценарии.
Код можно найти здесь: http://pastebin.com/RWTWNNCU
Вы можете использовать его следующим образом:
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
), потому что делегат не будет соответствовать тогда. Конечно, вы всегда можете изменить код в соответствии с вашими потребностями.