我理解为什么要使用以下语法来使用只读属性:
private int _MyInt;
public int MyInt
{
get { return _MyInt; }
}
这个例子可能不是最好的例子,因为我认为只读属性真的与readonly
变量一起发光,但这不是重点。我不明白的是为什么使用以下语法使用只写属性:
private int _MyInt;
public int MyInt
{
set { _MyInt = value; }
}
这是各种书籍和教程中描述只读属性的方式。如果你设置变量,你会在概念上在某个点读取它,至少在类的内部,但是要在类中内部读取它,你可以通过访问_MyInt
来读取它。我觉得违反了属性试图强制执行的封装精神。相反,为什么你不能仅使用具有不同访问修改属性的全部功能来访问它:
private int _MyInt;
public int MyInt
{
set { _MyInt = value; }
private get { return _MyInt; }
}
当然可以写出来
public int MyInt { set; private get; }
您仍然可以获得封装,但是限制其他类访问,因此它仍然只能写入外部类。
除非您确实想要分配给变量但从未实际访问变量,否则我肯定会对何时出现此需求感到好奇。
答案 0 :(得分:66)
我从来没有遇到过只写属性的有效用例。老实说,如果对于只写属性有一个有效的用例,我认为可以肯定地说解决方案的设计很糟糕。
如果您需要“只写”语义,则应使用方法。例如,另一个用户找到了一个用户对象的示例,该用户对象使用只写属性来设置密码。这是一个糟糕的设计:
class User
{
public string Password
{
set { /* password encryption here */ }
}
}
唉。这要好得多:
class User
{
public void SetPassword(string password)
{
/* password encryption here */
}
}
请参阅,读/写属性是一组旨在伪装成字段的方法。它们看起来和感觉就像一块田地。出于这个原因,只读属性是有意义的,因为我们习惯于拥有可以读取但不能更改的字段和变量。但是,没有相应的字段或变量构造可写但不可读。
这就是为什么我认为创建一个使用只写属性的API是不好的做法。它与我认为是C#中属性语法的主要目标背道而驰。
编辑:更多哲学......我相信课程具有功能性:它们提供了一个容器,用于保存和操作相关数据。以我们的User
类为例 - 该类将包含与系统中用户相关的所有信息。我们收集所有这些数据并为其命名: user 。通过这种方式,我们使用类来创建抽象。 User
是一种抽象,它允许我们推断构成用户的所有单独数据(密码,名称,生日等)。
现在有很好的抽象,并且有很糟糕的抽象。我认为只写属性是错误的抽象,因为你允许某人输入数据而不是读取数据。你为什么不允许这样做?很可能是因为传入的信息已经以某种方式进行了转换,使传球者无法理解。
所以这意味着按定义的只写属性必须创建调用者无法看到的副作用(因为如果他们能看到它们,那么就没有理由让属性写入 - 只要)。用于设置具有副作用的值的C#语言中的最佳构造是方法。
我强烈建议不要使用只写属性,因为API的使用者会发现它们令人困惑和令人沮丧。即使您为此语法找到了有效的用例,也无法证明其使用。
编辑:以下是.Net Framework Design Guidelines for Developing Class Libraries的正式推荐 - > Member Design Guidelines - > Property Design
不提供仅限设置的属性。
如果无法提供属性getter,请使用方法实现 相反的功能。方法名称应以Set开头 接着是属性名称......
答案 1 :(得分:16)
有趣的问题。
经过一些谷歌搜索后,this就是我能找到的:只写属性可用于在User
对象上设置密码。由于密码通常以散列形式存储,因此在设置密码之后(并且应该)无法检索密码。
答案 2 :(得分:10)
只写属性的一个用途是支持setter依赖注入。
假设我上课了:
public class WhizbangService {
public WhizbangProvider Provider { set; private get; }
}
WhizbangProvider不打算被外界访问。我永远不想与service.Provider
互动,这太复杂了。我需要一个像WhizbangService这样的类来充当外观。然而对于二传手,我可以这样做:
service.Provider = new FireworksShow();
service.Start();
该服务开始烟花汇演。或者你可能更喜欢看水和灯光表演:
service.Stop();
service.Provider = new FountainDisplay(new StringOfLights(), 20, UnitOfTime.Seconds);
service.Start();
依旧......
答案 3 :(得分:6)
我能看到的一个常见情况是,您是否只希望调用者能够获得您房产的转换版本。例如:
public int MyInt { set; }
public string MyIntAsString
{
get { return this.MyInt.ToString(); }
}
显然,这不是一个真实的例子,但你得到的是。
编辑:
在阅读了Thomas的示例用例之后,真正的“转换”类型场景可能是检索只写属性的加密版本:
private string password;
public string ClearPassword { set { this.password = value; }
public string EncryptedPassword
{
get { return Encrypt(password); }
}
答案 4 :(得分:2)
我读过的消息来源建议,如果你真的有一种只创建了一个只写属性的情况,你应该考虑把它变成一种方法。我通常同意。
答案 5 :(得分:1)
任何必须以单向方式流动的信息都是通过只写属性有用的。通常我们会编写类以便流出或流出的信息。在只写的情况下,您必须考虑信息应该只流入而不流出的情况。通常这涉及安全类型的信息,因为我们不希望消费者在检索安全信息时具有任何访问权限。密码,CC号等......
当信息不安全时,例如,当我们不介意任何人访问它时,我们会获得共同的获取/设置模式。 99.9%的时间是使用属性的方式。由于还有其他方法,同样容易实现这一点可以更加健壮,这更像是一种语义结构,留给对称而不是任何东西。其中一个罕见的情况是某些东西没有被取出而你的左边是“为什么他们会删除那个!”。
答案 6 :(得分:0)
我能想到的原因是因为_myInt显示在intellisense上下文菜单的顶部。
我认为访问_myInt根本不违反封装精神;它是一个类的私有成员字段。类中的任何代码都应该使用_myInt。毕竟,它是私人的。
对外界唯一重要的是班级的公共合同。 _myInt是班级本身的一个问题,不是面向公众的合同的一部分。
就像我说的那样 - 当您编码时,使用intellisense抓取_myInt会更容易。
答案 7 :(得分:0)
我确信这个功能只是用于仅使用get-only变量的symetry。正如Aphex模糊地暗示的那样,有些事情没有意义,只有在那里,因为它更容易留下而不是删除它。
答案 8 :(得分:0)
我有一个只写属性的原因。
在.NET Web服务中,我不得不创建遗留的.ASMX Web服务,因为URI是硬件中的硬编码。
我非常喜欢使用Ninject进行依赖注入,使用构造函数注入来满足类可能具有的所有依赖关系。在大多数情况下,这些依赖项将被声明为private readonly fieldName
并在构造函数中赋值。这样做的原因是类的依赖关系不是类的职责的一部分,它们是依赖关系。
然而,问题在于ASP.NET用于为Web服务创建类实例的工厂机制依赖于存在一个没有参数的构造函数,这会导致我的构造函数注入失败。
我本可以使用默认构造函数的调用来重定向到使用ServiceLocator(反)模式的重载构造函数,但我并不喜欢它。
使用Ninject Web扩展,我从Ninject.Web.WebServiceBase
继承了我的服务类并使用了属性注入,标记了Ninject将使用[Inject]
属性满足的属性。
回到关于注入服务是类的依赖关系的观点,而不是类所具有的职责,我不希望其他类访问这些依赖项属性,因此我将get属性标记为私有。