使用显式接口防止意外修改 C# 中的属性
我偶然发现了以前没有注意到的 C# 方法解析的一个功能。也就是说,当我显式实现支持 setter 的接口,并且隐式接口仅提供受保护集时,编译器在我调用它时会明智地遵循受保护集。因此,我获得了自动实现属性的大部分便利,但我可以防止不应该更改字段的客户意外修改字段。
例如,
virtual public DateTime CreatedOn { get; protected set; }
virtual public DateTime? ModifiedOn { get; protected set; }
#region IHaveUpdateDateFields Members
DateTime IHaveUpdateDateFields.CreatedOn
{
get
{
return this.CreatedOn;
}
set
{
this.CreatedOn = value;
}
}
DateTime? IHaveUpdateDateFields.ModifiedOn
{
get
{
return this.ModifiedOn;
}
set
{
this.ModifiedOn = value;
}
}
我的模型绑定代码不会意外设置日期,但我的 ORM 事件侦听器可以检查实现 IHaveUpdateDateFields 的实体,并在保留实体时设置日期。
我的问题是:
- 我是否依赖定义的行为,或者我是否保证所有 C# 编译器都会以这种方式解析方法?例如,我不想发现 C# 标准规定这种方法解析是未定义的,然后当我为 Mono 构建时意外地出现可怕的堆栈溢出。
- 有没有更好(最好是更简洁)的方法来做到这一点?我可以有一个传递给控制器的 ModelBinder 安全接口,但这似乎不会节省我的代码,而且我不认为它会提供一种透明的方法来最大限度地减少属性的意外修改。
I stumbled on a feature of C# method resolution that I didn't notice before. Namely, when I explicitly implement an interface that supports a setter, and the implicit interface only offers a protected set, the compiler sensibly defers to the protected set when I call it. So I get most of the convenience of auto-implemented properties, but I can prevent accidental modification of fields by clients who shouldn't be changing them.
As an example,
virtual public DateTime CreatedOn { get; protected set; }
virtual public DateTime? ModifiedOn { get; protected set; }
#region IHaveUpdateDateFields Members
DateTime IHaveUpdateDateFields.CreatedOn
{
get
{
return this.CreatedOn;
}
set
{
this.CreatedOn = value;
}
}
DateTime? IHaveUpdateDateFields.ModifiedOn
{
get
{
return this.ModifiedOn;
}
set
{
this.ModifiedOn = value;
}
}
Then my model binding code doesn't accidentally set the date, but my ORM event listener can check for entities that implement IHaveUpdateDateFields and set the date when persisting my entity.
My questions are:
- Am I relying on defined behavior, or am I guaranteed that all C# compilers will resolve methods this way? I don't want to discover that the C# standard says this kind of method resolution is undefined and then accidentally get a horrifying stack overflow when I build for Mono, for example.
- Is there a nicer (ideally terser) way to do this? I could have a ModelBinder-safe interface that I pass to my controller, but that doesn't seem like it would save me code and I don't think it would provide as transparent an approach of minimizing accidental modification of properties.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这是非常明确的定义;使用接口时,显式接口实现优先,否则常规属性生效(包括在 get/set 主体内)。
至于让它更整洁......我能提供的最好的办法就是重新格式化它以使其不那么冗长......
(另请注意,
this
是隐式和冗余的)顺便说一句 - 安全性是只是方便,而不是保证...外部调用者仍然可以使用您的界面,并且通常可以(ab)使用反射来跳过诸如
protected
之类的东西 - 或者甚至直接设置字段。That is perfectly well defined; the explicit interface implementations take priority when using the interface, and the regular property takes effect otherwise (including from within the body of the get/set).
As for making it tidier... the best I can offer is to reformat it to make it less verbose...
(note also that the
this
is implicit and redundant)As an aside - the safety is only a convenience, not a guarantee... external callers can still use your interface, and can usually (ab)use reflection to jump past mere things like
protected
- or even just set the fields directly.