使用显式接口防止意外修改 C# 中的属性

发布于 2024-08-11 08:37:39 字数 966 浏览 10 评论 0原文

我偶然发现了以前没有注意到的 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 的实体,并在保留实体时设置日期。

我的问题是:

  1. 我是否依赖定义的行为,或者我是否保证所有 C# 编译器都会以这种方式解析方法?例如,我不想发现 C# 标准规定这种方法解析是未定义的,然后当我为 Mono 构建时意外地出现可怕的堆栈溢出。
  2. 有没有更好(最好是更简洁)的方法来做到这一点?我可以有一个传递给控制器​​的 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:

  1. 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.
  2. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

千寻… 2024-08-18 08:37:39

这是非常明确的定义;使用接口时,显式接口实现优先,否则常规属性生效(包括在 get/set 主体内)。

至于让它更整洁......我能提供的最好的办法就是重新格式化它以使其不那么冗长......

DateTime IHaveUpdateDateFields.CreatedOn
{
    get { return CreatedOn; }
    set { CreatedOn = value; }
}

(另请注意, 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...

DateTime IHaveUpdateDateFields.CreatedOn
{
    get { return CreatedOn; }
    set { CreatedOn = value; }
}

(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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文