C # getter и setter стенография - PullRequest
       41

C # getter и setter стенография

14 голосов
/ 16 февраля 2011

Если мое понимание внутренней работы этой строки верно:

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 ]

24 голосов
/ 16 февраля 2011

Вам нужно разобраться с этим самостоятельно:

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.

11 голосов
/ 16 февраля 2011

Создайте декоратор IsDirty (шаблон дизайна), чтобы обернуть некоторые свойства, требующие функции флага isDirty.

2 голосов
/ 16 февраля 2011

Я собираюсь добавить к ответу Саймона Хьюза.Я предлагаю то же самое, но добавляю способ, позволяющий классу декоратора автоматически обновлять глобальный флаг 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 голосов
/ 16 февраля 2011

Вы можете сделать это простым или сложным.Это зависит от того, сколько работы вы хотите инвестировать.Вы можете использовать аспектно-ориентированное программирование для добавления аспекта с помощью ткача 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);

С уважением, Алоис Краус

1 голос
/ 16 февраля 2011

Я создал собственный класс 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. Это значение по умолчанию, поэтому его можно удалить.

UPDATE:

Очевидно, что это не сработает, когда вам нужно поддерживать несколько типов (кроме int), потому что тогда делегат не будет совпадать. Конечно, вы всегда можете изменить код в соответствии с вашими потребностями.

...