所以我和一位同事就一段代码进行了友好的争论:
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
我不同意。我觉得第一种方式更好,因为它使属性在类外保持只读。我的问题是,哪种方式是面向对象设计的最佳实践?
答案 0 :(得分:7)
您可以在属性上给自己一个private set
(在允许您使用属性语法的同时保留可见性或缺少可见性),但这并没有真正解决这一问题。
在课堂上,我说变量是公平的游戏。外面的任何地方,包括继承的类,应该get
和set
属性,但在声明的类中,我说可以分配私有成员。
答案 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));
}