Change Attribute's parameter at runtime

2019-01-03 06:25发布

I am not sure whether is it possible to change attribute's parameter during runtime? For example, inside an assembly I have the following class

public class UserInfo
{
    [Category("change me!")]
    public int Age
    {
        get;
        set;
    }
    [Category("change me!")]
    public string Name
    {
        get;
        set;
    }
}

This is a class that is provided by a third party vendor and I can't change the code. But now I found that the above descriptions are not accurate, and I want to change the "change me" category name to something else when i bind an instance of the above class to a property grid.

May I know how to do this?

10条回答
forever°为你锁心
2楼-- · 2019-01-03 06:29

Well you learn something new every day, apparently I lied:

What isn’t generally realised is that you can change attribute instance values fairly easily at runtime. The reason is, of course, that the instances of the attribute classes that are created are perfectly normal objects and can be used without restriction. For example, we can get the object:

ASCII[] attrs1=(ASCII[])
    typeof(MyClass).GetCustomAttributes(typeof(ASCII), false);

…change the value of its public variable and show that it has changed:

attrs1[0].MyData="A New String";
MessageBox.Show(attrs1[0].MyData);

…and finally create another instance and show that its value is unchanged:

ASCII[] attrs3=(ASCII[])
    typeof(MyClass).GetCustomAttributes(typeof(ASCII), false);
 MessageBox.Show(attrs3[0].MyData);

http://www.vsj.co.uk/articles/display.asp?id=713

查看更多
爱情/是我丢掉的垃圾
3楼-- · 2019-01-03 06:30

I really don't think so, unless there's some funky reflection that can pull it off. The property decorations are set at compile time and to my knowledge are fixed

查看更多
ゆ 、 Hurt°
4楼-- · 2019-01-03 06:30

You can change Attribute values at runtime at Class level (not object):

var attr = TypeDescriptor.GetProperties(typeof(UserContact))["UserName"].Attributes[typeof(ReadOnlyAttribute)] as ReadOnlyAttribute;
attr.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(attr, username_readonly);
查看更多
甜甜的少女心
5楼-- · 2019-01-03 06:31

You can subclass most of the common attributes quite easily to provide this extensibility:

using System;
using System.ComponentModel;
using System.Windows.Forms;
class MyCategoryAttribute : CategoryAttribute {
    public MyCategoryAttribute(string categoryKey) : base(categoryKey) { }

    protected override string GetLocalizedString(string value) {
        return "Whad'ya know? " + value;
    }
}

class Person {
    [MyCategory("Personal"), DisplayName("Date of Birth")]
    public DateTime DateOfBirth { get; set; }
}

static class Program {
    [STAThread]
    static void Main() {
        Application.EnableVisualStyles();
        Application.Run(new Form { Controls = {
           new PropertyGrid { Dock = DockStyle.Fill,
               SelectedObject = new Person { DateOfBirth = DateTime.Today}
           }}});
    }
}

There are more complex options that involve writing custom PropertyDescriptors, exposed via TypeConverter, ICustomTypeDescriptor or TypeDescriptionProvider - but that is usually overkill.

查看更多
贪生不怕死
6楼-- · 2019-01-03 06:36

In case anyone else walks down this avenue, the answer is you can do it, with reflection, except you can't because there's a bug in the framework. Here's how you would do it:

Dim prop As PropertyDescriptor = TypeDescriptor.GetProperties(GetType(UserInfo))("Age")
Dim att As CategoryAttribute = DirectCast(prop.Attributes(GetType(CategoryAttribute)), CategoryAttribute)
Dim cat As FieldInfo = att.GetType.GetField("categoryValue", BindingFlags.NonPublic Or BindingFlags.Instance)
cat.SetValue(att, "A better description")

All well and good, except that the category attribute is changed for all the properties, not just 'Age'.

查看更多
Ridiculous、
7楼-- · 2019-01-03 06:41

In the mean time I've come to a partial solution, derived from the following articles:

  1. ICustomTypeDescriptor, Part 1
  2. ICustomTypeDescriptor, Part 2
  3. Add (Remove) Items to (from) PropertyGrid at Runtime

Basically you would create a generic class CustomTypeDescriptorWithResources<T>, that would get the properties through reflection and load Description and Category from a file (I suppose you need to display localized text so you could use a resources file (.resx))

查看更多
登录 后发表回答