如何在运行时向属性添加属性

88
//Get PropertyDescriptor object for the given property name
var propDesc = TypeDescriptor.GetProperties(typeof(T))[propName];

//Get FillAttributes methodinfo delegate
var methodInfo = propDesc.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public |
                                                      BindingFlags.NonPublic)
    .FirstOrDefault(m => m.IsFamily || m.IsPublic && m.Name == "FillAttributes");

//Create Validation attribute
var attribute = new RequiredAttribute();
var  attributes= new ValidationAttribute[]{attribute};

//Invoke FillAttribute method
methodInfo.Invoke(propDesc, new object[] { attributes });

你好,我正在尝试使用上述代码在运行时添加验证属性。然而,我遇到了以下异常:

集合大小是固定的

5个回答

235

不要听别人说你做不到,如果你想的话,你可以竞选总统 :-)

为了方便起见,这是一个完全可行的例子

public class SomeAttribute : Attribute
{
    public SomeAttribute(string value)
    {
        this.Value = value;
    }

    public string Value { get; set; }
}

public class SomeClass
{
    public string Value = "Test";
}

[TestMethod]
public void CanAddAttribute()
{
    var type = typeof(SomeClass);

    var aName = new System.Reflection.AssemblyName("SomeNamespace");
    var ab = AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Run);
    var mb = ab.DefineDynamicModule(aName.Name);
    var tb = mb.DefineType(type.Name + "Proxy", System.Reflection.TypeAttributes.Public, type);

    var attrCtorParams = new Type[] { typeof(string) };
    var attrCtorInfo = typeof(SomeAttribute).GetConstructor(attrCtorParams);
    var attrBuilder = new CustomAttributeBuilder(attrCtorInfo, new object[] { "Some Value" });
    tb.SetCustomAttribute(attrBuilder);

    var newType = tb.CreateType();
    var instance = (SomeClass)Activator.CreateInstance(newType);

    Assert.AreEqual("Test", instance.Value);
    var attr = (SomeAttribute)instance.GetType()
        .GetCustomAttributes(typeof(SomeAttribute), false)
        .SingleOrDefault();
    Assert.IsNotNull(attr);
    Assert.AreEqual(attr.Value, "Some Value");
}

160
鼓舞人心的演讲,点赞! - Sjoerd222888
1
哦,看起来我们不能对枚举成员使用这种技术。我收到了一个错误,说父类是密封的... - Jerther
9
这会将一个属性添加到类而不是类的属性,对吗? - Denny
4
@Denny,那是正确的,但概念将保持不变。为了使其工作,属性必须是虚拟的,并且您需要发出一些IL代码。这个问题应该能帮助你解决问题:https://dev59.com/9F7Va4cB1Zd3GeqPN-KR。 - Jürgen Steinblock
7
该代码动态创建了一个继承自SomeClass的新类,并添加了class level属性SomeAttribute,创建了一个实例并验证了结果。就像你可以使用静态方法进行如下操作:[SomeAttribute("Some Value")] public class SomeClassProxy : SomeClass { }; var instance = new SomeClassProxy(); - Jürgen Steinblock
显示剩余9条评论

10

顶尖的答案很棒。最近,已经开发出一种库,将所有这些复杂性抽象化,并为您提供如下简单的内容:

var attributeType = typeof(CustomAAttribute);
var attributeParams = new object[] { "Jon Snow" };
var typeExtender = new TypeExtender("ClassA");
typeExtender.AddProperty("IsAdded", typeof(bool), attributeType, attributeParams);

要使用该库,请参阅如何安装和使用的详细信息 这里

免责声明:我开发了这个库,已在许多项目中使用它,效果很棒


6
但是您不能向现有属性添加属性,对吗? - tchelidze
这取决于您的使用情况。您可以使用TypeExtender扩展类,然后添加一个具有相同名称和签名的属性(属性隐藏),然后附加属性。 - undefined

5

使用我开发的FastDeepCloner

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

var prop = DeepCloner.GetFastDeepClonerProperties(typeof(test)).First();
prop.Attributes.Add(new JsonIgnoreAttribute());
// now test and se if exist 
prop = DeepCloner.GetFastDeepClonerProperties(typeof(test)).First();
bool containAttr = prop.ContainAttribute<JsonIgnoreAttribute>()
// or 
JsonIgnoreAttribute myAttr = prop.GetCustomAttribute<JsonIgnoreAttribute>();

我该如何进行深度克隆并将属性添加到类而不是属性中? - Sinaesthetic

0

它不起作用是因为FillAttributes方法期望一个IList类型的参数,而你正在传递一个数组。以下是MemberDescriptor.FillAttributes的实现:

protected virtual void FillAttributes(IList attributeList) { 
    if (originalAttributes != null) {
        foreach (Attribute attr in originalAttributes) {
            attributeList.Add(attr);
        } 
    }
}

正如您所看到的,FillAttributes 只是用您属性的所有属性填充 attributeList 参数。为了使您的代码工作,请将 var attributes= new ValidationAttribute[]{attribute}; 行更改为:

var attributes = new ArrayList { attribute };

这段代码与在运行时向属性添加属性无关。这是从类型中提取的“向PropertyDescriptor添加属性”,除非您正在尝试基于已经存在的类型构建运行时类型,否则没有意义。


-13

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接