只写属性是否有实际应用?
我不知道为什么我开始想这个,但现在我似乎无法停下来。
在C#中 – 也许还有很多其他的语言,我记得Delphi也曾经让你这样做 – 编写这个语法是合法的:
class WeirdClass { private void Hello(string name) { Console.WriteLine("Hello, {0}!", name); } public string Name { set { Hello(name); } } }
换句话说,该属性有一个setter,但没有getter ,它是只写的 。
我想我想不出有什么理由说这是非法的 ,但是我从来没有真正看到过它,而且我看起来在野外有一些相当辉煌的代码。 这似乎是一种代码味道; 编译器似乎应该给我一个警告:
CS83417:属性“名称”似乎是完全无用和愚蠢的。 不好的程序员! 考虑用一种方法replace。
但是也许我只是没有做足够长的时间,或者一直在狭窄的领域里工作,以便看到有效使用这样的结构的例子。
是否有真实的只写属性的例子,要么不能被直接的方法调用取代,要么变得不那么直观?
只写属性实际上是非常有用的,我经常使用它们。 这完全是关于封装 – 限制对对象组件的访问。 您经常需要为需要在内部使用的类提供一个或多个组件,但没有理由让其他类可以访问它们。 这样做只会让你的课更容易混淆(“我使用这个getter还是这个方法?”),更可能是你的课可能被篡改或被其真实的目的绕过。
请参阅“为什么getter和setter方法是邪恶的”这个有趣的讨论。 我并不像文章的作者那样硬着头皮,但我认为这是一件好事。 我通常使用setter,但很less使用getter。
我对这个问题的第一个反应是:“ java.util.Random#setSeed方法怎么样?
我认为只写属性在几个场景中是有用的。 例如,当你不想公开内部表示( 封装 ),同时允许改变对象的状态。 java.util.Random是这种devise的一个很好的例子。
代码分析(又名FxCop)的确给你一个诊断:
CA1044 :Microsoft.Design:由于属性“WeirdClass.Name”是只写的,因此可以添加一个属性获取器,其中的可访问性大于或等于其设置器,或者将此属性转换为方法。
我在XNA项目中有类似于以下的代码。 正如你所看到的, Scale是只写的,它是有用的(合理的)直观的,读取属性( get )对它没有意义。 当然可以用一种方法来替代,但是我喜欢它的语法。
public class MyGraphicalObject { public double ScaleX { get; set; } public double ScaleY { get; set; } public double ScaleZ { get; set; } public double Scale { set { ScaleX = ScaleY = ScaleZ = value; } } // more... }
只要你不应该读你写的东西,写一些东西是有用的。
例如,在屏幕上绘制东西时(这正是Windows中的桌面窗口pipe理器的function):
你当然可以绘制到屏幕上,但是你永远不需要读回数据(更不用说期望得到和以前一样的devise了)。
现在,无论是只写属性是有用的(而不是方法),我不知道他们多久使用。 我想你可以想象一个具有“BackgroundColor”属性的情况,在这种情况下写入它将设置屏幕的背景颜色,但是读取是没有意义的(必然)。
所以我不确定这个部分,但总的来说,我只想指出,在只写数据的情况下存在用例,而不会读取它。
一个用于只写属性的用途是支持setterdependency injection,它通常用于可选参数。
假设我有一堂课:
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();
等等….
如果该属性是在基类中定义的,这将变得特别有用。 如果你为这个属性select构造函数,你需要在任何派生类中写一个构造函数重载。
public abstract class DisplayService { public WhizbangProvider Provider { set; private get; } } public class WhizbangService : DisplayService { }
这里,构造函数注入的替代方法是:
public abstract class DisplayService { public WhizbangProvider Provider; protected DisplayService(WhizbangProvider provider) { Provider = provider ?? new DefaultProvider(); } } public class WhizbangService : DisplayService { public WhizbangService(WhizbangProvider provider) : base(provider) { } }
这种方法在我看来更加混乱,因为你需要类的一些内部工作,特别是,如果你传递null
给构造函数,你会得到一个合理的默认值。
就我而言,他们没有。 每次我使用只写属性作为快速入侵,我后来都后悔了。 通常我会得到一个构造函数或一个完整的属性。
当然,我试图certificate一个消极的,所以也许有一些我错过了。
我也不能停止想这个。 我有一个“只写”属性的用例。 我看不出有什么好办法。
我想构build一个C#属性,派生自ASP.NET MVC应用程序的AuthorizeAttribute。 我有一个服务(比如说,IStore)返回的信息,有助于决定是否应当授权当前用户。 因为构造函数注入不起作用
public AllowedAttribute: AuthorizeAttribute { public AllowedAttribute(IStore store) {...} private IStore Store { get; set; } ... }
使存储成为一个位置属性参数,但是IStore不是一个有效的属性参数types,编译器不会构build用它注释的代码。 我被迫退回到Property Setter Injection。
public AllowedAttribute: AuthorizeAttribute { [Inject] public IStore Store { private get; set; } ... }
除了属性设置器而不是构造器注入的所有其他坏事之外,该服务是只写属性。 足够糟糕的是,我不得不将暴露者暴露给不需要了解实现细节的客户端。 也不会让任何人有任何帮助让客户看清楚的地方。
我认为dependency injection的好处胜过这种情况下针对只写属性的指导方针,除非我错过了一些东西。
不,我可以想象任何不能被replace的情况,尽pipe可能有人认为它们更具可读性。
假设情况:
CommunicationDevice.Response = "Hello, World"
代替
CommunicationDevice.SendResponse("Hello, World")
主要工作将是执行IO副作用或validation。
有趣的是,VB .NET甚至为这种奇怪的属性获得了自己的关键字;)
Public WriteOnly Property Foo() As Integer Set(value As Integer) ' ... ' End Set End Property
尽pipe来自外部的许多“只写”属性实际上具有私有的获取者。
我最近在处理密码的应用程序工作。 (请注意,我并没有声称以下是一个好主意,我只是在描述我所做的。)
我有一个class级, HashingPassword
密码,其中包含一个密码。 构造函数将密码作为参数并将其存储在私有属性中。 鉴于这些对象之一,您可以获取密码的盐渍散列,或检查给定的盐渍散列的密码。 当然,没有办法从HashingPassword
对象中检索密码。
那么我还有其他的一些东西,我不记得那是什么东西。 让我们假装它是一个密码保护的香蕉。 Banana
类有一个名为Password
的set-only属性,它从给定的值创build一个HashingPassword
并将其存储在Banana
的私有属性中。 由于HashingPassword
的password
属性是私有的,所以没有办法为这个属性写一个getter。
那么为什么我有一个名为Password
的set-only属性,而不是一个名为SetPassword
的方法? 因为这是有道理的。 事实上,这个效果是设置Banana
的密码,如果我想设置一个Banana
对象的密码,我希望通过设置一个属性,而不是通过调用一个方法。
使用名为SetPassword
的方法不会有任何主要的缺点。 但是我也没有看到任何显着的优势。
我知道这已经有很长一段时间了,但我碰到它并且有一个有效的(imho)用例:
当你从ajax发布参数给webapi调用时,你可以简单地尝试填写参数类的属性并包含任何validation。
public int MyFancyWepapiMethod([FromBody]CallParams p) { return p.MyIntPropertyForAjax.HasValue ? p.MyIntPropertyForAjax.Value : 42; } public class CallParams { public int? MyIntPropertyForAjax; public object TryMyIntPropertyForAjax { set { try { MyIntPropertyForAjax = Convert.ToInt32(value); } catch { MyIntPropertyForAjax = null; } } } }
在JavaScript方面,您可以简单地填写参数,包括validation:
var callparameter = { TryMyIntPropertyForAjax = 23 }
这在这个例子中是安全的,但是如果你处理userinput它可能不确定你有一个有效的int值或类似的东西。
在MVP模式中,通常在视图上使用setter编写一个属性(不需要getter) – 只要演示者设置内容,属性将使用该值更新某个UI元素。
看到这里的一个小示范:
public partial class ShowMeTheTime : Page, ICurrentTimeView { protected void Page_Load(object sender, EventArgs e) { CurrentTimePresenter presenter = new CurrentTimePresenter(this); presenter.InitView(); } public DateTime CurrentTime { set { lblCurrentTime.Text = value.ToString(); } } }
演示者InitView方法简单地设置属性的值:
public void InitView() { view.CurrentTime = DateTime.Now; }
那么,从技术上来说,只有一个Setter属性本身没有什么问题。 二传手可以做一些validation,也许其他一些方法 – 公开或私人 – 可以依靠它。
例如:
class SomeClass { private int _someVar; SomeClass(int someVar) { if(someVar > 0) _someVar = someVar; } public int SomeVar { set { if(value > 0) _someVar = value; } } public string SomeFunction(){ return "This is an important function that uses _someVar " + _someVar; } }
所以声明本身可能是合法和有效的。 虽然你的方法体是一个巨大的代码气味,但是这不是编译器关心的工作。