Объединение нескольких атрибутов в один атрибут

В элементе управления я использую несколько свойств атрибута:

[Browsable(false)]
[Bindable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Obsolete("", true)]
public new Boolean AllowDrop;

Я использую эти свойства и для многих других свойств управления.

Мне интересно, есть ли способ уменьшить количество кода для записи каждый раз.

Было бы неплохо, если бы я мог объединить несколько атрибутов:

[Hidden(true)]
public new Boolean AllowDrop;

Если свойство Hidden будет включать все атрибуты выше. Таким образом, существует только одна строка кода.

Может быть, есть способ комбинировать атрибуты в макросе или что-то в этом роде?

Я знаю, что есть другие способы скрытия свойств, но я выбрал способ использования атрибутов.

Спасибо

Ответ 1

Объединение атрибутов может иметь смысл для контекста, который использует и интерпретирует атрибуты. Например, для тех контекстов, которые используют механизмы .Net Type Description, вы можете настроить описание типа, которое .Net возвращает потребителям.

Можно предоставлять собственные метаданные для типов, используя для этого стандартный механизм .Net, регистрируя собственный дескриптор типа для вашего объекта.

Идея будет работать таким образом, вы создаете собственный дескриптор типа для вашего типа. В дескрипторе пользовательского типа вы возвращаете дескрипторы пользовательских свойств для свойств вашего типа, а в дескрипторе свойства вы возвращаете пользовательский набор атрибутов для свойства.

Подход требует больше кода, но он действительно интересен и дает хорошее представление о том, как предоставить пользовательские метаданные для ваших типов:

Интерфейс IMetedataAttribute

Использование предоставляет стандартный способ создания MetaDataAttributes. Каждый атрибут, который реализует этот интерфейс, будет использоваться в качестве метаданных, и вместо атрибута будут использоваться те, которые он возвращает в методе Process:

public interface IMetadatAttribute
{
    Attribute[] Process();
}

Образец атрибута метаданных

Это образец атрибута метаданных, который возвращает некоторый атрибут при обработке атрибута:

public class MySampleMetadataAttribute : Attribute, IMetadatAttribute
{
    public Attribute[] Process()
    {
        var attributes = new Attribute[]{ 
            new BrowsableAttribute(false),
            new EditorBrowsableAttribute(EditorBrowsableState.Never), 
            new BindableAttribute(false),
            new DesignerSerializationVisibilityAttribute(
                    DesignerSerializationVisibility.Hidden),
            new ObsoleteAttribute("", true)
        };
        return attributes;
    }
}

Дескриптор свойства

Этот класс будет использоваться дескриптором настраиваемого типа для предоставления настраиваемого списка атрибутов для свойства:

public class MyPropertyDescriptor : PropertyDescriptor
{
    PropertyDescriptor original;
    public MyPropertyDescriptor(PropertyDescriptor originalProperty)
        : base(originalProperty) { original = originalProperty;}
    public override AttributeCollection Attributes
    {
        get
        {
            var attributes = base.Attributes.Cast<Attribute>();
            var result = new List<Attribute>();
            foreach (var item in attributes)
            {
                if(item is IMetadatAttribute)
                {
                    var attrs = ((IMetadatAttribute)item).Process();
                    if(attrs !=null )
                    {
                        foreach (var a in attrs)
                            result.Add(a);
                    }
                }
                else
                    result.Add(item);
            }
            return new AttributeCollection(result.ToArray());
        }
    }
    // Implement other properties and methods simply using return original
    // The implementation is trivial like this one:
    // public override Type ComponentType
    // {
    //     get { return original.ComponentType; }
    // }
}

Тип дескриптора

Это дескриптор типа, который предоставляет пользовательское описание для вашего типа. В этом примере он использует дескрипторы пользовательских свойств для предоставления пользовательских атрибутов, установленных для свойств вашего класса:

public class MyTypeDescriptor : CustomTypeDescriptor
{
    ICustomTypeDescriptor original;
    public MyTypeDescriptor(ICustomTypeDescriptor originalDescriptor)
        : base(originalDescriptor)
    {
        original = originalDescriptor;
    }
    public override PropertyDescriptorCollection GetProperties()
    {
        return this.GetProperties(new Attribute[] { });
    }
    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
                             .Select(p => new MyPropertyDescriptor(p))
                             .ToArray();
        return new PropertyDescriptorCollection(properties);
    }
}

Поставщик дескрипторов типов

Этот класс будет использоваться в атрибуте над вашим типом для представления дескриптора пользовательского типа, который мы создали как механизм метаданных для типа:

public class MyTypeDescriptionProvider : TypeDescriptionProvider
{
    public MyTypeDescriptionProvider()
        : base(TypeDescriptor.GetProvider(typeof(object))) { }

    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,
                                                            object instance)
    {
       ICustomTypeDescriptor baseDescriptor = base.GetTypeDescriptor(objectType, instance);
       return new MyTypeDescriptor(baseDescriptor);
    }
}

Образец класса

Вот мой пример класса, его свойство Name оформлено с использованием MySampleMetadataAttribute, и сам класс зарегистрирован для использования нашего поставщика дескрипторов пользовательских типов:

[TypeDescriptionProvider(typeof(MyTypeDescriptionProvider))]
public class MySampleClass
{
    public int Id { get; set; }
    [MySampleMetadataAttribue]
    [DisplayName("My Name")]
    public string Name { get; set; }
}

Чтобы увидеть результат, достаточно создать экземпляр класса и увидеть результат в PropertyGrid:

var o = new MySampleClass();
this.propertyGrid1.SelectedObject = o;

Некоторые примечания к ответу

  • Возможно, это не так просто, как вы ожидали для такой задачи. Но это работает.
  • Это длинный ответ, но содержит полный рабочий пример того, как вы можете применять дескрипторы типов к вашим типам для предоставления пользовательских метаданных.
  • Подход не будет работать для двигателей, которые используют отражение вместо описания типа. Но он полностью работает, например, с элементом управления PropertyGrid, который работает с описанием типа.