我在很多地方都读过,公开公开字段不是一个好主意,因为如果你以后想要更改属性,你将不得不重新编译使用你的类的所有代码。
但是,在不可变类的情况下,我不明白为什么你需要更改为属性 - 毕竟你不会在'set'中添加逻辑。
对此有任何想法,我错过了什么吗?
差异的例子,对于那些比文本更容易阅读代码的人:)
//Immutable Tuple using public readonly fields
public class Tuple<T1,T2>
{
public readonly T1 Item1;
public readonly T2 Item2;
public Tuple(T1 item1, T2 item2)
{
Item1 = item1;
Item2 = item2;
}
}
//Immutable Tuple using public properties and private readonly fields
public class Tuple<T1,T2>
{
private readonly T1 _Item1;
private readonly T2 _Item2;
public Tuple(T1 item1, T2 item2)
{
_Item1 = item1;
_Item2 = item2;
}
public T1 Item1 { get { return _Item1; } }
public T2 Item2 { get { return _Item2; } }
}
当然,您可以使用自动属性(public T1 Item1 { get; private set; }
),但这只会让您“同意不变”,而不是“保证不变性”......
答案 0 :(得分:12)
C#6.0现在支持自动属性初始值设定项。
自动属性初始值设定项允许直接分配属性 在他们的声明中。对于只读属性,它需要处理 确保物业不变的所有仪式。
您可以在构造函数中初始化只读属性或使用自动初始化程序
public class Customer
{
public Customer3(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string FirstName { get; }
public string LastName { get; }
public string Company { get; } = "Microsoft";
}
var customer = new Customer("Bill", "Gates");
您可以阅读有关自动属性初始值设定项here
的更多信息答案 1 :(得分:11)
属性明显遗漏,你不能写出像:
public T2 Item2 { get; readonly set; }
我甚至不确定readonly
是用来表示“只能在构造函数中设置”的最好的词,但这就是我们所坚持的。
这实际上是许多人要求的功能,所以我们希望很快就会在假设的新版C#中引入它。
答案 2 :(得分:4)
将来可能不需要向setter添加任何逻辑,但您可能需要向 getter 添加逻辑。
这足以让我使用属性而不是暴露字段。
如果我感觉严格,那么我会寻求完全不变的(明确的readonly
支持字段与暴露的getter和没有setter)。如果我感到懒惰,那么我可能会选择“同意的不变性”(与暴露的吸气剂和私人制定者的自动属性)。
答案 3 :(得分:1)
在 C#9 中,我们有 init 访问器,您可以使用它来代替 set 访问器。
var firstCar = new Car { Color = "Orange", Brand = "Mclaren" };
public class Car
{
public string Color { get; init; }
public string Brand { get; init; }
}
<块引用>
一个 init only 属性(或索引器)是通过使用 init 声明的 访问器代替集合访问器
<块引用>一个包含 init 访问器的实例属性被认为是 在以下情况下可设置,除非在本地 函数或 lambda:
答案 4 :(得分:0)
作为一种标准做法,我只关注你的第二个例子,当对象是公共的或易受攻击时,只能使用'readonly'来进行无意中的篡改。我在构建插件框架的当前项目中使用“商定的不变性”模型。显然,在商定的不变性的情况下,readonly
保护被删除。
仅在极少数情况下才会公开字段 - 公共字段,内部字段或其他字段。只是感觉不对,除非写一个属性{get;}比我愿意花费更多的时间。
答案 5 :(得分:0)
属性背后的想法是,即使你现在或以后不打算改变它们,你也可能需要采取一些不可预知的方式。假设您需要更改getter以进行某种计算或记录。也许你需要添加异常处理。很多潜在的原因。
还要考虑语义。如果T1是值类型而不是引用类型,则访问obj.Item1会在getter中返回_Item1的副本,而在没有getter的情况下访问Item1则不会检索副本。这意味着虽然Item1在内部可能是不可变的,但返回的值类型对象不是。我想不出为什么那会是好的事情,但这是一个区别。