如何将CustomAttribute动态添加到所有对象属性?

时间:2019-07-23 13:34:56

标签: c#

我有一个具有多种属性的对象:

public int num1 { get; set; }

public int num2 { get; set; }

public string str1 { get; set; }

public string str2 { get; set; }

这些属性位于动态生成的类中,因此它将擦除它们上的所有CustomAttributes。

我尝试添加

[Submit]
MyClass myObject

但是它并没有传播到我的对象属性中

有没有办法在c#内部动态地做到这一点?

2 个答案:

答案 0 :(得分:0)

我在理解您的问题时遇到了一些麻烦,但让我尝试澄清一下。

  

我有一个具有多个属性的对象   ...   它将删除所有CustomAttributes

按照C#的说法,格式为> hist(cost, plot=F) $breaks [1] -1.00 -0.95 -0.90 -0.85 -0.80 -0.75 -0.70 -0.65 -0.60 -0.55 -0.50 -0.45 [13] -0.40 -0.35 -0.30 -0.25 -0.20 -0.15 -0.10 -0.05 0.00 0.05 0.10 0.15 [25] 0.20 0.25 0.30 0.35 0.40 0.45 0.50 0.55 0.60 0.65 0.70 0.75 [37] 0.80 0.85 0.90 0.95 1.00 $counts [1] 108 43 32 20 22 21 19 20 19 17 16 19 8 19 23 17 15 10 18 [20] 22 15 19 14 15 18 16 21 11 18 20 16 35 23 24 24 20 23 33 [39] 37 107 的类成员称为“属性”,而不是“属性”。另一方面,“属性”是注释的C#实现,例如您要引用的自定义属性。

也就是说,我目前理解您是指您具有一个具有多个属性的自动生成的类。您希望每个属性都具有自己的自定义属性,但是如果您编辑类,则在下次生成类时将其删除,并且无法使类生成器包含自定义属性。

了解更多有关课程背景的知识可能会有所帮助。例如,它是如何生成的?如果是实体框架类,则以下SO问题可能会提供一些见解: Add data annotations to a class generated by entity framework。总的来说,是(或可以创建)生成的类<access> <type> <name> { get; set; }吗?如果是这样,那么您仍然可以按照上述问题的答案 viz 中的方法进行操作。使自己的部分类实现能够提供属性的自定义属性。

例如,如果您生成的类看起来(或可以使其看起来)像这样:

partial

您可以使用自定义注释编写部分类的另一部分,如下所示:

/// <auto-generated />
public partial class MyClass
{
    public int Num1 { get; set; }

    public int Num2 { get; set; }

    public string Str1 { get; set; }

    public string Str2 { get; set; }
}

同样,在不进一步了解您的情况的情况下,我不确定这是否能为您提供所需的信息,但我希望至少可以为您提供一个起点。

修改

如果该类不是局部类,则可以考虑将其生成的类包装为其包装属性使用custom属性的类。例如,

/// human generated
public partial class MyClass
{
    [Submit]
    public int Num1 { get; set; }

    [Submit]
    public int Num2 { get; set; }

    [Submit]
    public string Str1 { get; set; }

    [Submit]
    public string Str2 { get; set; }
}

编辑2

如果您希望有一个更具动态性的解决方案,但会付出一些设计和运行时复杂性的代价,则可以考虑以下SO问题:How to add property-level Attribute to the TypeDescriptor at runtime?。似乎解决了类似的问题-

  

实际上,它是为MS的“应用程序设置”生成的代码,因此您不能以任何属性扩展它。

我不会在这里完全重复Gman的解释,但从本质上讲,这种方法包括

  1. 获取类型(/// human generated public class MyClassWrapper { private readonly MyClass wrapped; public MyClassWrapper(MyClass wrapped) { this.wrapped = wrapped; } [Submit] public int Num1 { get => this.wrapped.Num1; set => this.wrapped.Num1 = value; } [Submit] public int Num2 { get => this.wrapped.Num2; set => this.wrapped.Num2 = value; } [Submit] public string Str1 { get => this.wrapped.Str1; set => this.wrapped.Str1 = value; } [Submit] public string Str2 { get => this.wrapped.Str2; set => this.wrapped.Str2 = value; } } )或类型MyClass的实例
  2. 使用myObject / TypeDescriptor.GetProvider(MyClass / myObject).GetTypeDescriptor(MyClass获取类型或对象的基线myObject)
  3. 使用此基线描述符构造他的ICustomTypeDescriptor
  4. 使用PropertyOverridingTypeDescriptor / MyClass遍历myObject / TypeDescriptor.GetProperties(MyClass的属性定义。使用myObject)根据当前属性的定义创建新的属性定义,添加自定义属性TypeDescriptor.CreateProperty(或在您的情况下为EditorAttribute),并使用构造的SubmitAttribute 3.使用新的属性定义。
  5. 用3中构造的PropertyOverridingTypeDescriptor构造他的TypeDescriptorOverridingProvider
  6. 使用PropertyOverridingTypeDescriptor将新的属性定义应用于MyClass / myObject

答案 1 :(得分:0)

将属性添加到动态生成的类的另一种方法是在代码生成管道中添加一个命令行应用程序。

这是一个如何使用Microsoft.CodeAnalysis.CSharp库重写C#代码文件的示例:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System;
using System.IO;

namespace SB.Common.ContractGenerator
{
    class SubmitAttributeAdder : CSharpSyntaxRewriter
    {
       public override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node)=>
            node.WithAttributeLists(
                node.AttributeLists.Count == 0
                    ? node.AttributeLists.Add(SyntaxFactory.AttributeList()
                         .AddAttributes(SyntaxFactory.Attribute(
                            SyntaxFactory.ParseName("Submit")))
                        // Add some whitespace to keep the code neat.
                        .WithLeadingTrivia(node.GetLeadingTrivia())
                        .WithTrailingTrivia(SyntaxFactory.Whitespace(Environment.NewLine)))
                    : node.AttributeLists);
    }

    class Program
    {
        static void Main(string[] args)
        {
            // The file name to be injected as a command line parameter
            var tree = CSharpSyntaxTree.ParseText(
                SourceText.From(File.ReadAllText("Test.cs")));

            File.WriteAllText("Test.cs",
                new SubmitAttributeAdder().Visit(tree.GetRoot()).ToString());
        }
    }
}

首先,将输入的C#代码文件解析为语法树,然后SubmitAttributeAdder类浏览所有已声明的类及其属性,以修改每个属性的属性列表。

之后,将修改后的语法树保存回同一文件。

此处,该应用仅在属性的属性列表为空的情况下添加Submit属性,但是可以轻松使逻辑更复杂-例如检查是否还有其他属性,为using <namespace>属性添加相应的Submit,依此类推。

运行应用程序之前的示例Test.cs文件:

namespace MyProject
{
    class MyClass
    {
        public int num1 { get; set; }

        public int num2 { get; set; }

        public string str1 { get; set; }

        public string str2 { get; set; }
    }
}

...以及之后:

namespace MyProject
{
    class MyClass
    {
        [Submit]
        public int num1 { get; set; }

        [Submit]

        public int num2 { get; set; }

        [Submit]

        public string str1 { get; set; }

        [Submit]

        public string str2 { get; set; }
    }
}