【问题标题】:How to add attributes to a base class's properties如何将属性添加到基类的属性
【发布时间】:2020-09-07 22:19:01
【问题描述】:

我有几个这样的模型类:

public class MyModelBase
{
    public string Name { get; set; }
}

public class MyModel : MyModelBase
{
    public string SomeOtherProperty { get; set; }
}

MyModel 如何向 Name 属性添加 [Required] 属性?

【问题讨论】:

  • 你问的是数据注释吗?如果不是,那么您询问的是哪个[Required] 属性。

标签: c#


【解决方案1】:

将父类中的属性声明为虚拟:

public class MyModelBase
{
    public virtual string Name { get; set; }
}

public class MyModel : MyModelBase
{
    [Required]
    public override string Name { get; set; }

    public string SomeOtherProperty { get; set; }
}

或者您可以使用 MetadataType 来处理验证(只要您在谈论 DataAnnotations...否则您会遇到上面的示例):

class MyModelMetadata
{
    [Required]
    public string Name { get; set; }

    public string SomeOtherProperty { get; set; }
}

[MetadataType(typeof(MyModelMetadata))]
public class MyModel : MyModelBase
{
    public string SomeOtherProperty { get; set; }
}

【讨论】:

  • 如果您有其他不想失去的行为,这就是要走的路。 +1
  • 您现在必须小心基础构造函数中潜在的虚拟调用。
  • 根据MSDN MyModel 应该是部分的。
  • 请注意,用新的自动属性覆盖自动属性会导致支持字段的数量增加一倍(即~双倍实例大小)。更可怕的是,您可能会无意中从基类的字段初始值设定项中丢失数据! (数据在基础支持字段中,但覆盖实现只查看它们的 自己的 支持字段。)请参阅 Justin Minnaar 的答案以获得更好的覆盖方法。
【解决方案2】:

尝试使用metadata class。它是一个单独的类,使用属性引用,允许您将数据注释间接添加到模型类。

例如

[MetadataType(typeof(MyModelMetadata))]
public class MyModel : MyModelBase {
  ... /* the current model code */
}


internal class MyModelMetadata {
    [Required]
    public string Name { get; set; }
}

ASP.NET MVC(包括 Core)通过 ModelMetadataTypeAttribute 为其属性提供类似的支持,例如 FromQuery

【讨论】:

  • 有人愿意评论否决票吗?从技术上讲,这对我来说是正确的(甚至添加了与我原来的答案类似的内容)。
  • 我的猜测是这仅适用于 DataAnnotations 命名空间中的属性,因此不是通用解决方案。
  • MSDNMyModel 应该是部分的。
  • @d.popov,MSDN 并没有说 MyModel 应该总是部分的。 MSDN 使用部分类,因为他们假设 MyModel 部分是由生成 MyModel 类的工具生成的。
  • 请注意,我们不能在不同的程序集中有两个部分类。
【解决方案3】:

我注意到,这些答案都没有真正正确地调用基本 Name 属性。覆盖应该像下面这样写,以便您没有新属性的单独值。

public class MyModelBase
{
    public virtual string Name { get; set; }
}

public class MyModel : MyModelBase
{
    [Required]
    public override string Name { get { return base.Name; } set { base.Name = value; }

    public string SomeOtherProperty { get; set; }
}

【讨论】:

  • 我看不到,为什么有必要。如果你说的是真的,这段代码不应该工作,但它工作:csharppad.com/gist/c5db27fb3b90f961a2da。我猜,那个编译器会自动处理被覆盖的自动属性。
  • @Spook 我认为只有在基类中有一些自定义实现(如 setter 中的 RaisePropertyChanged)时才有必要。如果只在子类中使用 auto 属性,则不会调用基类中的 getter/setter。
【解决方案4】:

您可以通过“new”关键字重载基本属性。

public class MyModelBase
{
     public string Name { get; set; }
}

public class MyModel : MyModelBase
{
     [Required]
     public new string Name {get; set;}
     public string SomeOtherProperty { get; set; }
}

【讨论】:

  • 这个“有效”但破坏了多态性。如果您编写以下代码,“某事”将为空。我的模型 m = 新的我的模型(); m.Name = "废话"; MyModelBase mb = m; var something = mb.Name;
  • 所以你说新关键字重载很危险?那它有什么用呢?
  • 我从未说过它很危险,但鉴于这里有其他更好的解决方案,不建议使用 new 。我通常非常谨慎地使用 new,并且仅在需要覆盖返回类型时使用。
  • 您确定要使用“过载”这个词吗?我认为您正在尝试谈论“覆盖”。如果您不完全了解自己在做什么,那么用 new 覆盖可能会产生“危险”的影响。你问“那它是做什么用的”的方式表明,潜在危险的东西没有实际目的。斧头很危险,但它也可以用来砍木头。 “new”关键字在某些情况下是“危险的”,但对于批量替换派生类中的特定功能也很有用。
  • new 在您想要更改继承类中的属性类型时很有用,这可能具有非常有用的特定目的,但它也会创建一组额外的支持字段,如果您使用自动属性并且不管理基字段的值,如果您尝试使用基类方法中的属性,数据可能会丢失。如果您使用 new,您要么需要知道基类不需要使用派生类中设置的值,要么您需要像 Justin Minnaar 的回答那样设置基属性。
猜你喜欢
  • 1970-01-01
  • 2010-11-20
  • 2021-09-26
  • 1970-01-01
  • 1970-01-01
  • 2013-09-02
  • 1970-01-01
  • 2018-03-16
  • 1970-01-01
相关资源
最近更新 更多