我应该在类中使用Field或Property来设置值

时间:2009-05-14 15:04:45

标签: c#

所以我和一位同事就一段代码进行了友好的争论:

public sealed class NewObject
{
    private string _stuff = string.Empty;

    public string Stuff
    {
        get { return GetAllStuff(); }
    }

    private string GetAllStuff()
    {
        //Heavy string manipulation of _stuff
    }

    public NewObject(string stuffToStartWith)
    {
        _stuff = stuffToStartWith;
    }

    public static NewObject operator +(NewObject obj1, NewObject obj2)
    {
        if (obj1 == null)
            throw new ArgumentNullException();

        if (obj2 == null)
            throw new ArgumentNullException();

        NewObject result = new NewObject(string.Empty);
        result._stuff = String.Concat(obj1._stuff, obj2._stuff);

        return result;
    }
}

争论在于操作员覆盖。我的同事认为,除了构造函数之外,设置私有字段的值不是最好的编程习惯。我的同事提出的解决方案是将Stuff属性的名称重构为AllStuff,并添加一个Stuff和{{1}的属性get访问器并在运算符覆盖中使用新的set属性。看起来像这样:

Stuff

我不同意。我觉得第一种方式更好,因为它使属性在类外保持只读。我的问题是,哪种方式是面向对象设计的最佳实践?

5 个答案:

答案 0 :(得分:7)

您可以在属性上给自己一个private set(在允许您使用属性语法的同时保留可见性或缺少可见性),但这并没有真正解决这一问题。

在课堂上,我说变量是公平的游戏。外面的任何地方,包括继承的类,应该getset属性,但在声明的类中,我说可以分配私有成员。

答案 1 :(得分:2)

你是对的

错误......详细说明,您的私人变量可以随意使用。如果有人对你做了一个改变对象值的操作(特别是+),那么修改构造函数之外的值没有错。那就是他们的私密性。

除非你想要它不可变......

更新
我越是想到它,我越相信你的同事将“私人”变量与“常量”变量混淆 - 或者可能合并这两个概念。没有理由私有变量必须在对象的整个生命周期中保持不变,这正是你的朋友似乎暗示的。 const用于不变,private仅用于对象,它们是两种截然不同的模式。

UPDATE2
此外,如果突然你的对象不仅仅是一个字符串 - 并且变量交织在一起(想象一个字符串对象,有char *和len,并且必须一起维护),他的设计就会崩溃。你想要的最后一件事是用户必须处理对象的内部变量。让对象成为对象并保持其自己的内部值并向用户呈现单个实体。

答案 2 :(得分:2)

一般问题与合同政策有关。

(公共集)属性的概念是,当调用它时,除了改变状态的语义概念之外,还可以采取其他动作。例如,调用setter可能会触发事件,触发外围设备等等。

你的同事说,不使用房产,你就是踩到合同,不会发生任何事件。

所以你应该从同事的角度来做这件事:

this.Prop = CalculateSomeValue();
if (this.Prop < kPropMin) {
    this.Prop = kPropMin;
}
else if (this.Prop > kPropMax * 2) {
    this.Prop = kPropMax * 2;
}
this.Prop = this.Prop / 2;

现在,这是一个人为设计的案例,但我只是在一个可能的重量级属性中获得了三次,在该组中最多三次,其中一个可能是非法的(设置为kHighLimit / 2) )。我可以通过使用本地并在最后一次调用集合来解决这个问题。不过,我宁愿把这个领域搞得一团糟。

我认为更好的方法是务实地采用它:当且仅当你想要调用集合或get的所有副作用时才使用你的类中的属性,否则就要遵守属性的精神。 / p>

- 澄清 - 通过遵守财产的精神,让我们说我的设置属性如下:

bool PropValueOutOfRange(int val) {
    return val < kPropMin || val > kPropMax;
}

public int Prop {
    set {
        if (PropValueOutOfRange(value))
            throw new ArgumentOutOfRangeException("value");
        if (PropValueConflictsWithInternalState(value))
            throw new ArgumentException("value");
        _prop = value;
        NotifyPeriperalOfPropChange(_prop);
        FirePropChangedEvent(/* whatever args might be needed */);
    }
}

在这里,我已经考虑了许多蹩脚的细节,但这让我可以重复使用它们。所以现在我有信心触摸私有字段_prop,因为我有相同的基础设施,以确保我保持在范围内并通知外围设备并解雇事件。

这让我可以写下这段代码:

_prop = CalculateSomeValue();

if (_prop < kPropMin)
    _prop = kPropMin;
else if (_prop > kPropMax * 2)
    _prop = kPropMax;

_prop /= 2;

NotifyPeripheralOfPropChange();
FirePropChangedEvent();

我使用与用于构建属性的工具相同的工具,因此我在属性的精神下工作。我保持正确的范围(但不要抛出 - 我知道更好,我是实施者),击中外围设备和发射事件,我做得很周到,可读,有效 - 而不是不分青红皂白。

答案 3 :(得分:1)

我不知道他的方法会带来什么好处。

答案 4 :(得分:0)

我个人更喜欢没有字段,因此我想使用自动实现的private属性而不是private字段和public-get private-set属性{1}}只读属性。
如果我必须向属性添加代码,我仍然只使用属性访问器内的字段,并在其他地方使用getter和setter,包括构造函数。
如果我需要public字段,我也必须使用字段,但C#4.0将引入只读属性。


此外,我可以通过使用以下代码避免整个问题。

readonly

我首选的实现方式是这样的。

public static NewObject operator +(NewObject obj1, NewObject obj2)
{
    return new NewObject(String.Concat(obj1.Stuff, obj2.Stuff));
}