C# getter和setter简写

16

如果我对这行代码的内部运作方式理解正确:

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。

这是否可能?还是我只能接受将类中的代码量增加三倍的事实?


用IronPython吧,它在CLR上运行,但支持面向方面的编程,这正是你所需要的。 :) - bzlm
如果您正在使用Visual Studio,您可能可以创建一个代码片段来帮助节省时间。 - ebrown
5个回答

25

你需要自己处理这个问题:

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

目前还没有语法可以在保持自动属性机制的同时添加自定义逻辑到setter中。因此,你需要自行编写带有后备字段的代码。

这是一个常见问题 - 例如,在实现INotifyPropertyChanged时。


1
这正是他试图避免做的事情 :) - asdfjklqwer
1
@Nathan:是的,但这确实是他必须要做的——自动属性无法与自定义逻辑一起使用。话虽如此,我的实现方式与原帖作者不同——我使用字段而不是自动属性作为后备字段。 - Reed Copsey
没问题,我刚意识到我应该在那里加上一个if(_MyInt != value)检查,所以我只能用冗长的方式来做 :) - William
@barlop 这只使用了一个字段和一个自动属性。你可以使用新的语法,但它是相同的。 - Reed Copsey
@ReedCopsey 我不确定是否理解正确,但是经过进一步尝试,看起来如果只有getter或者同时有getter和默认setter,那么你只需要getter/setter字段,而不需要另一个字段。但是,如果想要一个非默认setter的getter和setter,那么似乎需要另一个字段。因为一个非空的setter会有例如_abc=value这样的形式,所以你需要那个_abc字段。也许一些非默认/非空的“gets”也需要显式地写入属性。 - barlop
显示剩余2条评论

11
创建一个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。当我的任何属性被更新时,我希望被更新的那个属性将IsDirty设置为true。你的代码还能实现这个吗? - William
2
这只适用于一个属性。每个属性都有自己的IsDirty标志。如果您想要所有属性的单个IsDirty标志,则需要修改修饰符以持有对IsDirty标志的引用,以使其适用于每个属性。 - Simon Hughes

2
你可以简单或复杂地实现它。这取决于你想要投入多少工作。你可以使用面向方面的编程通过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);

您好,Alois Kraus


2

我要在Simon Hughes的回答上做出补充。我建议做同样的事情,但增加一种方式来让装饰器类自动更新全局的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;
    }

}

1
我创建了一个自定义的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)时,这种方法不起作用,因为委托将无法匹配。当然,您可以根据需要调整代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接