Как добавить видимую информацию в классы C# с помощью атрибутов?
Я пытаюсь добавить информацию в интерфейс C#, и я пытаюсь добавить эту информацию, используя атрибуты.
Скажем, например, что у меня везде есть параметры int, и некоторые из этих параметров int являются "идентификаторами строк", а некоторые - "идентификаторами столбцов". Я хотел бы иметь возможность сделать это:
namespace RowIdTest1
{
using System;
public class Class1
{
public int Get([RowId] int r, [ColumnId] int c) { return 3; }
public void Set([RowId] int r, [ColumnId] int c, int x) { return; }
}
[AttributeUsageAttribute(AttributeTargets.All)]
public class RowIdAttribute : System.Attribute { }
[AttributeUsageAttribute(AttributeTargets.All)]
public class ColumnIdAttribute : System.Attribute { }
}
так что, когда другой программист использует мою DLL и просматривает Class1 в Visual Studio, он или она увидит это в окне "Class1 (из метаданных)":
namespace RowIdTest1
{
public class Class1
{
public Class1();
public int Get([RowId] int r, [ColumnID] int c);
public void Set([RowId] int r, [ColumnID] int c, int x);
}
}
Когда я пытаюсь это сделать, атрибуты вообще не отображаются.
Я знаю, что вы можете делать такие вещи с ///- комментариями.
Я знаю, что вы можете использовать класс System.ComponentModel.DescriptionAttribute для чего угодно (хотя описания методов будут появляться, а описания параметров не будут появляться).
Я делаю что-то совершенно странное с атрибутами C#? Не подходит? Безумный?
5 ответов
Я делаю что-то совершенно странное с атрибутами C#?
Да.
Вы пытаетесь использовать атрибут для выражения факта бизнес-домена о параметре. Это не то, для чего нужны атрибуты. Атрибуты предназначены для выражения фактов о механизме языка программирования. То есть, когда вы говорите:
[Obsolete]
class BlackAndWhiteTelevision {}
Это говорит о том, что сам класс устарел и что вы больше не должны его использовать, потому что класс был заменен другим классом. Не используйте устаревший атрибут, чтобы утверждать, что предмет физического мира, который представляет класс, больше не может быть произведен и продан с прибылью.
То, что конкретный параметр представляет идентификатор строки, является фактом о бизнесе вашего метода, а не о механизме метода. Вы должны поместить атрибуты в параметры метода, которые выражают такие вещи, как "этот параметр имеет значение по умолчанию" или "этот параметр должен быть упорядочен по значению при вызове через COM-взаимодействие" или "этот параметр будет записан до того, как он будет считан из" - это факты о параметре как переменной в языке программирования, а не факты о том, как параметр моделирует некоторую концепцию в вашем бизнес-процессе.
Для получения дополнительной информации о том, как использовать атрибуты для выражения фактов о механизмах вместо бизнес-доменов, см. Мою статью на эту тему и комментарии к ней:
http://blogs.msdn.com/b/ericlippert/archive/2009/02/02/properties-vs-attributes.aspx
Я делаю что-то совершенно странное с атрибутами C#? Не подходит? Безумный?
В общем случае причина использования атрибутов заключается в том, что вы хотите, чтобы определенная информация о классе или его членах была доступна вашей программе через отражение во время выполнения. Это не соответствует этой идее, поэтому я бы сказал, что это неправильное использование атрибутов.
Я думаю, что более предпочтительный способ дать программисту тонкий намек, что вы хотите, чтобы параметр представлял rowId, - это использовать имя параметра:
public int Get(int rowId, int columnId)
Соедините это с /// комментариями, как вы упомянули, и у вас есть довольно эффективный способ рассказать потребителям вашего класса, каковы ваши ожидания.
Наконец, если вы действительно хотите предотвратить случайное предоставление аргументов в неправильном порядке, вы можете использовать строгую типизацию.
public struct RowId {
private int _rowId;
public int Value{get{return _rowId;}}
public RowId(int rowId) {_rowId = rowId;}
}
public int Get(RowId rowId, ColumnId columnId)
Это сделало бы почти невозможным для кого-то случайно перепутать входные данные. (Обратите внимание, что я не рекомендую этот шаблон: он вводит много накладных расходов для очень небольшой выгоды.)
Нет, ты не можешь. Почему бы просто не назвать аргументы
public int Get(int rowId, int columnId)
?
Я должен сказать, что не очень хорошо это понимаю. Часто ли пользователи вашей библиотеки (я полагаю) вынуждены декомпилировать ваши классы для просмотра метаданных? Почему бы просто не написать XML-документацию для интерфейса и вместо этого отправить ее вместе с вашей библиотекой?
Я не знаю, если это "безумие". Если это не работает как есть, возможно, назначьте атрибуты вложенным элементам внутри некоторой структуры данных, а затем, возможно, передайте структуру данных в качестве параметра функции. Просто одна идея.