题
我正在C#中创建一个网络聊天客户端作为辅助项目。除了简单的文本消息,我还有可以输入到输入TextBox的斜杠前缀命令。我使用模块化方法创建一个包含所有各种命令的枚举,然后用属性装饰这些命令。
这些属性指定可以输入什么slash-prefixed命令来触发命令,以及主命令标识符和命令用法的任何别名。
示例:
public enum CommandType : byte
{
[PrimaryIdentifier("file"),
AdditionalIdentifier("f"),
CommandUsage("[<recipient>] [<filelocation>]")]
FileTransferInitiation,
[PrimaryIdentifier("accept"),
AdditionalIdentifier("a")]
AcceptFileTransfer,
// ...
}
当我尝试在主命令中允许多个别名时出现问题。我尝试了这两种方法:允许重复 AdditionalIdentifier
属性,或者在 AdditionalIdentifier
中创建构造函数参数a params string []
。
对于前者,我通过使用 AttributeUsage
修饰属性类并将 AllowMultiple
设置为true来实现它。虽然这确实实现了我正在寻找的东西,但我觉得除了其他属性之外,除了其他属性之外,还有几行别名可能会非常嘈杂。
后者也有效,但它会生成编译器警告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合规性警告是合理的。
编辑:进一步思考这个问题,你在这些枚举上有很多属性。您可能需要考虑创建一个抽象的Command类,并将标识符,用法等公开为该类的属性;然后派生具体类型的Command,从那些属性返回适当的值。这可能还允许您将处理逻辑移动到那些Command对象中,而不是打开枚举值。
其他提示
你也可以使用“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 for Roles。在内部,属性将字符串解析为数组,以便在属性类中使用,但它允许您轻松设置配置。
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,
...
}
另一种方法是让属性将一个字符串数组作为构造函数参数 - 这样,你就可以让编译器为你解析数组(在应用属性时以更多的goop为代价) :
[Identifiers(new string[] {"Bill", "Ben", "Ted"})]
实施&amp; amp;的一个简单的例子使用这种技术看起来像这样:
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);
}
}
}
}