Сокращение и сборщик С#

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

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 при изменении поля.

Возможно ли это или мне просто нужно смириться с тем, чтобы утроить количество кода в моих классах?

Ответ 1

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

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.

Ответ 2

Создайте декоратор 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);
    }
}

Ответ 3

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

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

Ответ 4

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

Теперь вы можете заставить класс decorator автоматически обновить другое свойство 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;
    }

}

Ответ 5

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