Атрибуты C # - массивы или дубликаты?
-
05-07-2019 - |
Вопрос
Я создаю клиент сетевого чата на C # в качестве побочного проекта.В дополнение к простым текстовым сообщениям у меня также есть команды с префиксом косой черты, которые можно вводить в текстовое поле ввода.Я использовал модульный подход, создав перечисление, содержащее все различные команды, а затем украсив эти команды атрибутами.
Атрибуты указывают, какую команду с префиксом косой черты можно ввести для запуска команды, а также любые псевдонимы основного идентификатора команды и способ ее использования.
Пример:
public enum CommandType : byte
{
[PrimaryIdentifier("file"),
AdditionalIdentifier("f"),
CommandUsage("[<recipient>] [<filelocation>]")]
FileTransferInitiation,
[PrimaryIdentifier("accept"),
AdditionalIdentifier("a")]
AcceptFileTransfer,
// ...
}
Моя проблема возникает, когда я пытаюсь разрешить несколько псевдонимов для основной команды.Я попробовал это двумя способами:разрешив дубликаты AdditionalIdentifier
атрибут, или путем создания аргумента конструктора в AdditionalIdentifier
a params string[]
.
В первом случае я реализовал это, украсив класс атрибутов AttributeUsage
и настройка AllowMultiple
к истинному.Хотя это действительно достигает того, что я ищу, я чувствую, что очень быстро может стать очень шумно иметь несколько строк псевдонимов в дополнение к другим атрибутам.
Последнее также работает, однако оно генерирует предупреждение компилятора CS3016, и говорит, что такой подход не соответствует CLS.Очевидно, что это не обязательно мешает мне продолжать им пользоваться, но я научился всегда рассматривать предупреждения как ошибки.
Мой фактический вопрос заключается в том, должен ли я игнорировать свои возражения с дубликатами и просто идти вперед и использовать их, или есть какое-то другое решение, которое можно было бы использовать?
Спасибо.
Решение
Лично я бы предпочел подход AllowMultiple:Я не думаю, что "шум" будет такой уж большой проблемой, если у вас действительно нет множества идентификаторов для каждой команды.Но если вам это не нравится и вы хотите оставаться совместимым с CLS, другим решением было бы предоставить перегруженные конструкторы для AdditionalIdentifierAttribute:
public AdditionalIdentifierAttribute(string id) { ... }
public AdditionalIdentifierAttribute(string id1, string id2) { ... }
public AdditionalIdentifierAttribute(string id1, string id2, string id3) { ... }
Недостатком является то, что это ограничивает вас заранее определенным количеством идентификаторов.
Тем не менее, соответствие CLS на самом деле является важным фактором только в том случае, если вы создаете библиотеку, которую, вероятно, будут использовать другие (и, в частности, с других языков).Если этот тип или библиотека являются внутренними для вашего приложения, то разумно игнорировать предупреждения о соответствии CLS.
Редактировать: Размышляя дальше об этом, у вас есть довольно много атрибутов в этих перечислениях.Возможно, вы захотите вместо этого создать абстрактный командный класс и предоставить информацию об идентификаторах, использовании и т.д.как свойства этого класса;затем выведите конкретные типы команд, которые возвращают соответствующие значения из этих свойств.Потенциально это также позволяет вам переместить логику обработки в эти командные объекты вместо включения значения enum.
Другие советы
Вы также могли бы использовать "псевдонимы params string[]" в конструкторе, чтобы разрешить список переменных аргументов:
[AttributeUsage(AttributeTargets.Method)]
class TestAttribute : Attribute
{
public TestAttribute(params string[] aliases)
{
allowedAliases = aliases;
}
public string[] allowedAliases { get; set; }
}
Это позволило бы вам сделать:
[Test("test1", "test2", "test3")]
static void Main(string[] args)
Почему бы не иметь один атрибут с несколькими свойствами?Пусть свойство для псевдонима принимает вид списка, разделенного запятыми.Это подход, который они используют в MVC для таких вещей, как AuthorizeAttribute для ролей.Внутренне свойство преобразует строку в массив для удобства использования в классе атрибутов, но оно позволяет вам легко настроить свою конфигурацию.
public class IdentifierAttribute
{
public string Name { get; set; }
public string Usage { get; set; }
private string[] aliasArray;
private string aliases;
public string Aliases
{
get { return this.aliases; }
set
{
this.aliases = value;
this.aliasArray = value.Split(',').Trim();
}
}
}
Затем используйте его как:
public enum CommandType : byte
{
[Identifer( Name = "file", Aliases = "f", Usage = "..." )]
FileTransferType,
...
}
Еще один подход состоял бы в том, чтобы атрибут принимал массив строк в качестве параметра конструктора - таким образом, вы заставляете компилятор анализировать массив за вас (за счет немного большего количества ошибок при применении атрибута), таким образом:
[Identifiers(new string[] {"Bill", "Ben", "Ted"})]
Быстрый и грязный пример реализации и использования такой техники выглядит следующим образом:
using System;
using System.Collections.ObjectModel;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
SomeClass.TellMeAboutYourself();
}
}
public class Identifiers : Attribute
{
private string[] names;
public Identifiers(string[] someNames)
{
names = someNames;
}
public ReadOnlyCollection<string> Names { get { return new ReadOnlyCollection<string>(names); } }
}
[Identifiers(new string[] {"Bill", "Ben", "Ted"})]
static class SomeClass
{
public static void TellMeAboutYourself()
{
Identifiers theAttribute = (Identifiers)Attribute.GetCustomAttribute(typeof(SomeClass), typeof(Identifiers));
foreach (var s in theAttribute.Names)
{
Console.WriteLine(s);
}
}
}
}