Использование класса System.Attribute
-
21-08-2019 - |
Вопрос
Я оказался в ситуации, когда использование класса System.Attribute показалось (на первый взгляд) хорошей идеей.
У меня есть объект, который нужно напечатать в приложении, и мне нужна метка перед каждым свойством (или просто строка перед ним).Я мог бы жестко запрограммировать каждое свойство, например:
Console.WriteLine("Color:"+obj.color);
Console.WriteLine("Size:"+obj.size);
И так по каждому свойству.Но вместо этого я пытался создать код, в котором эту «метку» не нужно было бы жестко запрограммировать, чтобы я мог динамически печатать каждое свойство.
Я получил что-то подобное, используя класс System.Attribute:
public class MyObject
{
[MyCustomLabel("Color:")]
public string Color;
[MyCustomLabel("Size:")]
public string Size;
//etc...
}
Итак, вот моя проблема:получить значение этого атрибута не невозможно, но это недружелюбно, поскольку для этого мне пришлось использовать некоторое отражение.
Я не особо боюсь использовать отражение, но мне показалось, что я использовал атрибуты для чего-то, для чего оно не было создано.
Интересно, где лучше всего использовать атрибуты и действительно ли это подходит для их использования.
Решение
Атрибуты и отражение идут рука об руку.За исключением некоторых атрибутов компилятора/среды выполнения, их невозможно использовать, не задумываясь над кодом.
Тем не менее, ваш подход разумен, и вы можете взглянуть на атрибуты в System.ComponentModel
пространство имен, которое имеет ряд классов для украшения свойств полезными метаданными.
Другие советы
Вы на правильном пути.
Кроме того, для этой цели уже создан атрибут [DisplayName], который присутствует в .NET начиная с версии 2.0.
http://msdn.microsoft.com/en-us/library/system.comComponentmodel.displaynameattribute.aspx
Если вы просто пишете в консоль, т.е.это вывод в стиле отладки, тогда вам нужна минимальная вероятность ошибки опечатки/копирования.
На самом деле это сбивает с толку, но очень эффективно на местах звонков:
public static void WriteNameAndValue<T,TValue>(
this TextWriter tw, T t,
Expression<Func<T, TValue>> getter)
{
var memberExpression = getter.Body as MemberExpression;
if (memberExpression == null)
throw new ArgumentException("missing body!");
var member = memberExpression.Member;
tw.Write(member.Name);
tw.Write(": ");
if (member is FieldInfo)
{
tw.Write(((FieldInfo)member).GetValue(t));
}
else if (member is PropertyInfo)
{
tw.Write(((PropertyInfo)member).GetValue(t, null));
}
}
public static void WriteNameAndValueLine<T,TValue>(
this TextWriter tw, T t,
Expression<Func<T, TValue>> getter)
{
WriteNameAndValue<T,TValue>(tw, t, getter);
tw.WriteLine();
}
тогда ты сможешь написать
t.Foo = "bar";
t.Bar = 32.5;
Console.Out.WriteNameAndValueLine(t, x => x.Foo);
Console.Out.WriteNameAndValueLine(t, x => x.Bar);
// output
// Foo: bar
// Bar: 32.5
Если вы хотите, чтобы это было более настраиваемым во время выполнения с помощью ресурсов и с учетом локализации, вы можете это сделать, но вместо этого я бы рассмотрел другой, более стандартизированный подход, если бы это было вероятным требованием.
P.S.если вы хотите пофантазировать, вы можете заменить переключатель FieldInfo/PropertyInfo на
tw.Write(getter.Compile()(t));
а затем вы также можете проверить MethodInfo в выражении (или разрешить произвольные лямбда-выражения и просто вставить номер строки или какой-либо другой общий текст.Я предлагаю не идти по этому пути, его использование уже сбивает с толку, а также может вызвать нежелательную нагрузку на то, что должно быть простым методом журналирования.
именно так работает сериализация, поэтому я бы сказал, что ваш подход разумен.Другой способ сделать это — создать словарь PropertyNames и их заголовков, а затем найти заголовок на основе имени свойства.
У меня есть объект, который нужно распечатать в приложении, и мне нужна метка перед каждым свойством (или просто строка перед ним).
Почему вы думаете об использовании атрибутов в вашем случае?Где вы печатаете эти объекты в своем приложении.
Может быть, просто реализовать Нанизывать в вашем объекте(ах)
Например.
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("PropertyX: ");
sb.AppendLine(this.PropertyX);
// get string from resource file
sb.Append(Resources.FileName);
sb.Append(": ");
sb.AppendLine(this.FileName);
sb.Append("Number: ");
sb.AppendLine(this.Number.ToString());
return sb.ToString();
}