Добавление дополнительных атрибутов к каждому свойству класса

Скажем, у меня есть класс с любым количеством свойств любого типа

public class Test
{
public string A {get;set;}
public object B {get;set;}
public int C {get;set;}
public CustomClass D {get;set;}
}

Я хочу, чтобы все объекты внутри этого класса имели понятие "ошибки" и "предупреждения". Например, в зависимости от некоторых условий в другом классе, я могу установить предупреждение в свойстве A, а затем отобразить эту информацию в графическом интерфейсе. GUI не беспокоит меня; скорее, как мне установить это предупреждение? Я хотел бы иметь возможность сделать что-то вроде:

Для каждого свойства в классе добавьте свойство "Warning" и "Error", которое я могу сделать.

Test t = new Test();
t.A.Warning = "This is a warning that the GUI will know to display in yellow".
t.B.Error = null;

Каков наилучший способ сделать это? Было бы неплохо, если бы я мог добавить пользовательский атрибут к каждому свойству класса, который добавит эти дополнительные свойства и позволит мне получить доступ к ним в ясной форме.

Я видел решения, которые добавляют словарь к родительскому классу (Test), а затем передают строки, соответствующие именам свойств, или используют отражение, чтобы получить имя свойства и передать его, но я бы предпочел что-то более чистое.

Ответ 1

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

Что-то вроде этого должно работать

Сначала вам нужно создать свой класс атрибутов

[AttributeUsage(AttributeTargets.All/*, AllowMultiple = true*/)]
public class WarningAttribute : System.attribute
{
   public readonly string Warning;

   public WarningAttribute(string warning)
   {
      this.Warning = warning;
   }    
}

Подробнее Чтение Здесь

Используйте его как таковой

[WarningAttribute("Warning String")]
public string A {get;set;}

Затем используйте его как Статья MSDN

public static string Warning(this Object object) 
{
    System.Attribute[] attrs = System.Attribute.GetCustomAttributes(object);

    foreach (System.Attribute attr in attrs)
        {
            if (attr is WarningAttrbiute)
            {
                return (WarningAttribute)attr.Warning;
            }
        } 
}

Затем, если у вас есть элемент, к которому вы хотите получить доступ к предупреждению, вы можете просто вызвать

test.A.Warning;

Если вы хотите установить строку предупреждения, вы можете реализовать какой-то набор для этого немного более чисто. Потенциально путем установки вторичного объекта или типа свойства.


Альтернативным методом для этого было бы вместо использования string и object вы могли бы создать собственный универсальный тип для обработки этого свойства.

Что-то вроде

public class ValidationType<T>
{
   public T Value {get; set;}
   public string Warning {get; set;}
   public string Error {get; set;}

   public ValidationType(T value)
   {
      Value = value;
   }
}

Использовать как

var newWarning = new ValidationType<string>("Test Type");
newWarning.Warning = "Test STring";
Console.WriteLine("newWarning.Value");

Ответ 2

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

Что-то вроде:

public static void Warning(this object target, string message)
{
...
}

public static String GetWarning(this object target)
{
}

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

Ответ 3

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

Таким образом, вы можете предоставить более подробную информацию, вы можете удалить их, когда они были показаны пользователю, и, если вы отправляете свои данные по "проводнику" (веб-сервисам, службам wcf), ошибки может перемещаться с вашими объектами, а не требовать специальной обработки для отправки атрибутов.

Ответ 4

Во-первых, атрибуты не могут изменять объект, на который они были помещены. Если вы не объедините их с АОП. Я бы предложил использовать класс оболочки вокруг класса, наследуя его, если классы не запечатаны. Если они запечатаны, вам придется написать фактический класс-оболочку, передав все операции, кроме тех, которые вы добавляете, в частное поле, которое является экземпляром класса, который вы обертываете.

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