【问题标题】:Is it possible to add an attribute to a property in a partial class?是否可以将属性添加到部分类中的属性?
【发布时间】:2015-12-27 13:36:12
【问题描述】:

我不认为这是可能的,但由于我没有从 MSDN 得到明确的说明,我觉得最好问问。假设我们有一个如下的类。

public partial class Hazaa
{
  public int Shazoo { get; set; }
}

然后,我希望将 Shazoo 归为 SuperCool 我希望在另一个文件中这样做。由于我使用的是部分类,因此我可以按如下方式添加新属性。

public partial class Hazaa
{
  [SuperCool]
  public int Wheee { get; set; }
}

但是我可以通过在第一个示例中编写代码来为第一个示例中声明的属性赋予属性吗?我怀疑这是可能的,但我很乐意接受纠正。如果是这样,语法是什么?

【问题讨论】:

    标签: c# .net attributes partial-classes


    【解决方案1】:

    根据您的要求,您可以考虑使用以下选项:

    注意: 您可以通过这种方式注册的属性实际上并不是您的类属性,但大多数框架(如 ASP.NET MVC)将它们用作您的类原生属性。

    如果您想添加数据注释属性,特别是在 ASP.NET MVC 项目中,您会发现这种方式很有帮助。

    对于不支持MetadataTypeAttribute 的其他框架,例如Windows Forms,您可以使用AssociatedMetadataTypeTypeDescriptionProvider 简单地添加支持。

    解决方案不限于数据注释属性,您可以使用对您的库和框架有意义的所有类型的属性。

    如何定义附加属性?

    您可以创建一个元数据类,其中包含由适当属性修饰的原始类的属性,然后用MetadataType 属性修饰部分类并为您的原始类引入元数据类。

    如何查看这些属性的影响?

    ASP.NET MVC 这样的框架使用这些属性,就像它们在您的原始类中定义的一样。

    您还可以将AssociatedMetadataTypeTypeDescriptionProvider 注册为您的原始类型的提供者,以便其他可能希望使用TypeDescriptor 来获取有关您的类型的信息的框架或组件。

    它们真的是我的类属性吗?

    请注意,这样,属性确实不属于您的原始类,但是对于大多数框架,例如使用TypeDescriptor 获取类型信息的ASP.NET MVCWindows Forms,它们的行为就像您的类原始属性。

    所以如果你想使用反射来获取一个属性的属性,你是看不到它们的,但是如果你使用TypeDescriptor机制,你就可以看到它们。

    一个例子

    Hazaa 类:

    public partial class Hazaa
    {
        public int Shazoo { get; set; }
    }
    

    Hazaa 元数据类

    [MetadataType(typeof(HazaaMetadata))]
    public partial class Hazaa
    {
    }
    
    public class HazaaMetadata
    {
        [DisplayName("Shazoo Name")]
        public int Shazoo { get; set; }
    }
    

    ASP.NET MVC 用法

    您无需执行任何其他操作即可使DisplayName 工作,您只需使用Html.LabelforHtml.DisplayNameFor 即可查看效果。它将显示“Shazoo 名称”作为标签文本。

    Windows 窗体使用

    在您的应用程序中的某些地方(如表单加载、main、...)以这种方式注册提供程序:

    var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Hazaa));
    TypeDescriptor.AddProvider(provider, typeof(Hazaa));
    

    因此,您将看到 PropertyGridDataGridView 使用“Shazoo 名称”作为属性和列标题的标题。

    【讨论】:

    • 这太广泛了,在我的情况下没有用。尽管如此,这似乎是一个有趣的贡献,并且在没有实际测试过的情况下,我相信你的话,呵呵。绝对要为雄心勃勃的方法 +1!
    • 感谢您的反馈,这种方法在 ASP.NET MVC 或使用代码生成的解决方案中大量使用。这样,您可以在不触及类本身的情况下向类添加一些属性,特别是在使用工具(t4、ef、...)生成主类的情况下。
    • 是吗?我现在实际上正在做 ASP.NET,自动生成的代码既棒又糟糕。也许我最终还是会使用你的建议,但作为人类,我更喜欢为我创造的大部分东西,呵呵。
    • 如果您使用 ASP.NET MVC,您可能会使用 [MetadataType(typeof(HazaaMetadata))],这样您就不需要注册提供程序,因为 MVC 的 DataAnnotation Metadata Provider 会查找您的 HazaaMetadata 类并且会用它。它是如此简单可爱:)
    • 这个解决方案在 ASP.NET MVC 中使用时非常棒,但是有一些限制。如果由于某种原因您需要在代码中手动检查属性的属性,c# 会很高兴地通知您没有属性(至少没有通过使用 MetadataType 属性包含的属性)。
    【解决方案2】:

    不,你不能。

    您只能将属性附加到您在那里声明的成员上,除非该成员也被声明为部分(以便您可以在其他地方重新实现它),否则您不能将属性附加到另一个部分文件中声明的成员。

    【讨论】:

    • (1) 该死...(2) 部分成员?正如您肯定意识到的那样,第一部分是自动生成的。我可以强制它从我控制的文件中分离出来而不自动生成被改变吗? (3) +1 为简洁起见。
    • 怀疑,实现部分成员有特殊的语法要求,例如必须指定 partial 关键字并且没有访问修饰符。
    • 没有像partial 这样的成员,使用[MetadataType] 属性的其他答案似乎是你能得到的最接近的答案
    【解决方案3】:

    当然,您可以使用元数据来做到这一点,如下所示:

    public partial class Hazaa : EntityBase
    {
        public int Shazoo { get; set; }
    }
    
    [MetadataTypeAttribute(typeof(HazaaMetadata))]
    public partial class Hazaa : EntityBase
    {
        internal sealed class HazaaMetadata
        {
            [SuperCool]
            public int Shazoo { get; set; }
        }
    }
    

    【讨论】:

      【解决方案4】:

      确保小数序列化为 2 位小数的最佳方法是 tp:

      1. 使用 XlmAttributeOverides 忽略十进制字段,即不对其进行序列化

      var xmlAttributeOverrides = new XmlAttributeOverrides(); var ctrlSumAttributes = new XmlAttributes { XmlIgnore = true }; xmlAttributeOverrides.Add(typeof(Sepa.GroupHeader39), "CtrlSum", ctrlSumAttributes); var ser = new XmlSerializer( typeof( Sepa.Document ), xmlAttributeOverrides);

      1. 使用部分类添加被忽略字段的字符串序列化

      公共部分类 GroupHeader39 { [XmlElement("CtrlSum")] 公共字符串 CtrlSumString { 获取 => CtrlSum.ToString("F2"); set { /* 需要工作 */ } } }

      我希望这有助于解决这个问题。

      【讨论】: