I have the following type of code sample across one of my projects...
[Obfuscation(Exclude = true)]
[UsedImplicitly]
public DelegateCommand<object> OpenXCommand { get; private set; }
I am finding the attributes are adding a lot of "noise" to the code - I also see it in a way violating the DRY principle since I may have several properties like this in one class, all with the same attribute decoration.
Q: Is there some way I can set up an attribute that will represent a combination of sub attributes?
Ideally I would like something that looks like this..
[MyStandardCommandAttribute]
public DelegateCommand<object> OpenXCommand { get; private set; }
I have not implemented my own attributes before so I am unsure if this is possible. Any suggestions?
No. Your one attribute cannot "be" Obfuscation and UsedImplicitly at the same time (there's no multiple inheritance in C#).
Code looking for e.g. UsedImplicitlyAttribute has no way of knowing that MyStandardCommandAttribute is supposed to represent UsedImplicitlyAttribute (unless you control all of the code using all of these attributes).
Unfortunately, there's no way to do this in C#.
But, if you control the places that read these attributes (with reflection), you can do it by convention.
For example, you can have a marker interface that will "annotate" your attribute with the attributes it proxies (sounds like a meta-attribute):
public interface AttributeProxy<T>
where T : Attribute {}
public class MyStandardCommandAttribute :
Attribute,
AttributeProxy<ObfuscationAttribute>,
AttributeProxy<UsedImplicitlyAttribute> {}
(Of course, you also have to match the right AttributeUsages. And you can't set properties on the proxied attributes like this.)
Now, you could go a step further and use an IL manipulation library, like Mono.Cecil, to actually transfer the attributes appropriately in a post-compilation step. In this case, it would work even if it weren't you reflecting on these attributes.
Update: still in the reflect-your-own-attributes scenario, you can use the below code to get to proxied attributes, even setting properties values:
public interface IAttributeProxy {
Attribute[] GetProxiedAttributes();
}
public class MyStandardCommandAttribute : Attribute, IAttributeProxy {
public Attribute[] GetProxiedAttributes() {
return new Attribute[] {
new ObfuscationAttribute { Exclude = true },
new UsedImplicitlyAttribute()
};
}
}
Use this extension method on your reflection code:
public static object[] GetCustomAttributesWithProxied(this MemberInfo self, bool inherit) {
var attributes = self.GetCustomAttributes(inherit);
return attributes.SelectMany(ExpandProxies).ToArray();
}
private static object[] ExpandProxies(object attribute) {
if (attribute is IAttributeProxy) {
return ((IAttributeProxy)attribute).GetProxiedAttributes().
SelectMany(ExpandProxies).ToArray(); // don't create an endless loop with proxies!
}
else {
return new object[] { attribute };
}
}
Related
On a control I am using multiple attribute properties:
[Browsable(false)]
[Bindable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Obsolete("", true)]
public new Boolean AllowDrop;
I am using those properties on a lot of the other control properties as well.
I am wondering if there is a way to reduce the amount of code to write each time.
It would be nice if I could combine multiple attributes like this:
[Hidden(true)]
public new Boolean AllowDrop;
Where the Hidden Property would include all the attributes above. So there is only 1 single line of code.
Maybe there is also a way to combine the attributes in a macro or something?
I am aware that there are other ways of hiding properties but I chose the way of using attributes.
Thanks
It depends to the framework which is using the attribute.
Combining attributes can be meaningful in order to the context which uses and interprets attributes. For example for those contexts which use .Net Type Description mechanisms you can customize the type description which .Net returns to consumers.
It's possible to provide custom metadata for types using the standard .Net mechanism for that purpose, registering a custom type descriptor for your object.
The idea will work this way, you create a custom type descriptor for your type. In the custom type descriptor, you return custom property descriptors for the properties of your type and in the property descriptor, you return a custom set of attributes for the property.
The approach requires more code, but it's really interesting and shares some good idea about how to provide custom metadata for your types:
IMetedataAttribute Interface
The usage is providing an standard way to create MetaDataAttributes. Each attribute which implements this interface will be used as metadata and instead of the attribute, those one which it returns in Process method will be used:
public interface IMetadatAttribute
{
Attribute[] Process();
}
Sample MetadataAttribute
It's a sample metadata attribute which returns some attribute instead when processing the attribute:
public class MySampleMetadataAttribute : Attribute, IMetadatAttribute
{
public Attribute[] Process()
{
var attributes = new Attribute[]{
new BrowsableAttribute(false),
new EditorBrowsableAttribute(EditorBrowsableState.Never),
new BindableAttribute(false),
new DesignerSerializationVisibilityAttribute(
DesignerSerializationVisibility.Hidden),
new ObsoleteAttribute("", true)
};
return attributes;
}
}
Property Descriptor
This class will be used by the custom type descriptor to provide a custom list of attributes for the property:
public class MyPropertyDescriptor : PropertyDescriptor
{
PropertyDescriptor original;
public MyPropertyDescriptor(PropertyDescriptor originalProperty)
: base(originalProperty) { original = originalProperty;}
public override AttributeCollection Attributes
{
get
{
var attributes = base.Attributes.Cast<Attribute>();
var result = new List<Attribute>();
foreach (var item in attributes)
{
if(item is IMetadatAttribute)
{
var attrs = ((IMetadatAttribute)item).Process();
if(attrs !=null )
{
foreach (var a in attrs)
result.Add(a);
}
}
else
result.Add(item);
}
return new AttributeCollection(result.ToArray());
}
}
// Implement other properties and methods simply using return original
// The implementation is trivial like this one:
// public override Type ComponentType
// {
// get { return original.ComponentType; }
// }
}
Type Descriptor
This is the type descriptor which provides a custom description for your type. In this example it uses custom property descriptors to provide custom attributes set for the properties of your class:
public class MyTypeDescriptor : CustomTypeDescriptor
{
ICustomTypeDescriptor original;
public MyTypeDescriptor(ICustomTypeDescriptor originalDescriptor)
: base(originalDescriptor)
{
original = originalDescriptor;
}
public override PropertyDescriptorCollection GetProperties()
{
return this.GetProperties(new Attribute[] { });
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
.Select(p => new MyPropertyDescriptor(p))
.ToArray();
return new PropertyDescriptorCollection(properties);
}
}
Typedescriptor Provider
This class will be used in the attribute above your type to introduce the custom type descriptor which we created as the metadata engine for the type:
public class MyTypeDescriptionProvider : TypeDescriptionProvider
{
public MyTypeDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(object))) { }
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,
object instance)
{
ICustomTypeDescriptor baseDescriptor = base.GetTypeDescriptor(objectType, instance);
return new MyTypeDescriptor(baseDescriptor);
}
}
Sample Class
Here is my sample class which its Name property is decorated using MySampleMetadataAttribute and the class itself is registered to use our custom type descriptor provider:
[TypeDescriptionProvider(typeof(MyTypeDescriptionProvider))]
public class MySampleClass
{
public int Id { get; set; }
[MySampleMetadataAttribue]
[DisplayName("My Name")]
public string Name { get; set; }
}
To see the result it's enough to create an instance of the class and see the result in PropertyGrid:
var o = new MySampleClass();
this.propertyGrid1.SelectedObject = o;
Some notes on answer
Probably it's not as simple as you expected for such task. But it's working.
It's a lengthy answer, but contains a complete working example of how you can apply type descriptors to your types to provide custom metadata.
The approach will not work for engines which use reflection instead of type description. But it's completely working with for example PropertyGrid control which works with type description.
The best way for me to do this, is by using Metalama (modern rewrite of PostSharp for new .NET releases).
It is absolutely the best framework for doing AOP in .NET from the same guys that did PostSharp. It is still in preview, but Metalama 1.0 will be released in a week or 2, and in next year, it will probably get most of features found in PostSharp... And it has a nice community on Slack and the authors of this Metalama framework are super supportive, they helped me with each question I had, and I had a lot of them already :D
And so this library is perfect for creating custom aspects, but could easily be used for this merging of attributes :) It will be even better then the approach above, because once you see transformed file (using Metalama Diff Preview - you gotta install Metalama extension to VS), then you will actually see all those original attributes there, in a transformed file :)
And so this is how easily I will merge 3 attributes into 1 with Metalama:
Metalama easy merging of attributes 1
(This would be for aspect attributes created by Metalama)
Or, for other attributes (from other libraries), that don't need to do the aspect work, it would by like this: (And this is probably what you want to use, not the first example...):
On a control I am using multiple attribute properties:
[Browsable(false)]
[Bindable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Obsolete("", true)]
public new Boolean AllowDrop;
I am using those properties on a lot of the other control properties as well.
I am wondering if there is a way to reduce the amount of code to write each time.
It would be nice if I could combine multiple attributes like this:
[Hidden(true)]
public new Boolean AllowDrop;
Where the Hidden Property would include all the attributes above. So there is only 1 single line of code.
Maybe there is also a way to combine the attributes in a macro or something?
I am aware that there are other ways of hiding properties but I chose the way of using attributes.
Thanks
It depends to the framework which is using the attribute.
Combining attributes can be meaningful in order to the context which uses and interprets attributes. For example for those contexts which use .Net Type Description mechanisms you can customize the type description which .Net returns to consumers.
It's possible to provide custom metadata for types using the standard .Net mechanism for that purpose, registering a custom type descriptor for your object.
The idea will work this way, you create a custom type descriptor for your type. In the custom type descriptor, you return custom property descriptors for the properties of your type and in the property descriptor, you return a custom set of attributes for the property.
The approach requires more code, but it's really interesting and shares some good idea about how to provide custom metadata for your types:
IMetedataAttribute Interface
The usage is providing an standard way to create MetaDataAttributes. Each attribute which implements this interface will be used as metadata and instead of the attribute, those one which it returns in Process method will be used:
public interface IMetadatAttribute
{
Attribute[] Process();
}
Sample MetadataAttribute
It's a sample metadata attribute which returns some attribute instead when processing the attribute:
public class MySampleMetadataAttribute : Attribute, IMetadatAttribute
{
public Attribute[] Process()
{
var attributes = new Attribute[]{
new BrowsableAttribute(false),
new EditorBrowsableAttribute(EditorBrowsableState.Never),
new BindableAttribute(false),
new DesignerSerializationVisibilityAttribute(
DesignerSerializationVisibility.Hidden),
new ObsoleteAttribute("", true)
};
return attributes;
}
}
Property Descriptor
This class will be used by the custom type descriptor to provide a custom list of attributes for the property:
public class MyPropertyDescriptor : PropertyDescriptor
{
PropertyDescriptor original;
public MyPropertyDescriptor(PropertyDescriptor originalProperty)
: base(originalProperty) { original = originalProperty;}
public override AttributeCollection Attributes
{
get
{
var attributes = base.Attributes.Cast<Attribute>();
var result = new List<Attribute>();
foreach (var item in attributes)
{
if(item is IMetadatAttribute)
{
var attrs = ((IMetadatAttribute)item).Process();
if(attrs !=null )
{
foreach (var a in attrs)
result.Add(a);
}
}
else
result.Add(item);
}
return new AttributeCollection(result.ToArray());
}
}
// Implement other properties and methods simply using return original
// The implementation is trivial like this one:
// public override Type ComponentType
// {
// get { return original.ComponentType; }
// }
}
Type Descriptor
This is the type descriptor which provides a custom description for your type. In this example it uses custom property descriptors to provide custom attributes set for the properties of your class:
public class MyTypeDescriptor : CustomTypeDescriptor
{
ICustomTypeDescriptor original;
public MyTypeDescriptor(ICustomTypeDescriptor originalDescriptor)
: base(originalDescriptor)
{
original = originalDescriptor;
}
public override PropertyDescriptorCollection GetProperties()
{
return this.GetProperties(new Attribute[] { });
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
.Select(p => new MyPropertyDescriptor(p))
.ToArray();
return new PropertyDescriptorCollection(properties);
}
}
Typedescriptor Provider
This class will be used in the attribute above your type to introduce the custom type descriptor which we created as the metadata engine for the type:
public class MyTypeDescriptionProvider : TypeDescriptionProvider
{
public MyTypeDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(object))) { }
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,
object instance)
{
ICustomTypeDescriptor baseDescriptor = base.GetTypeDescriptor(objectType, instance);
return new MyTypeDescriptor(baseDescriptor);
}
}
Sample Class
Here is my sample class which its Name property is decorated using MySampleMetadataAttribute and the class itself is registered to use our custom type descriptor provider:
[TypeDescriptionProvider(typeof(MyTypeDescriptionProvider))]
public class MySampleClass
{
public int Id { get; set; }
[MySampleMetadataAttribue]
[DisplayName("My Name")]
public string Name { get; set; }
}
To see the result it's enough to create an instance of the class and see the result in PropertyGrid:
var o = new MySampleClass();
this.propertyGrid1.SelectedObject = o;
Some notes on answer
Probably it's not as simple as you expected for such task. But it's working.
It's a lengthy answer, but contains a complete working example of how you can apply type descriptors to your types to provide custom metadata.
The approach will not work for engines which use reflection instead of type description. But it's completely working with for example PropertyGrid control which works with type description.
The best way for me to do this, is by using Metalama (modern rewrite of PostSharp for new .NET releases).
It is absolutely the best framework for doing AOP in .NET from the same guys that did PostSharp. It is still in preview, but Metalama 1.0 will be released in a week or 2, and in next year, it will probably get most of features found in PostSharp... And it has a nice community on Slack and the authors of this Metalama framework are super supportive, they helped me with each question I had, and I had a lot of them already :D
And so this library is perfect for creating custom aspects, but could easily be used for this merging of attributes :) It will be even better then the approach above, because once you see transformed file (using Metalama Diff Preview - you gotta install Metalama extension to VS), then you will actually see all those original attributes there, in a transformed file :)
And so this is how easily I will merge 3 attributes into 1 with Metalama:
Metalama easy merging of attributes 1
(This would be for aspect attributes created by Metalama)
Or, for other attributes (from other libraries), that don't need to do the aspect work, it would by like this: (And this is probably what you want to use, not the first example...):
I have an attribute that I am using to decorate object properties with. The attribute identifies the properties as needing validation to be performed on them. I am essentially implementing the Strategy Pattern and building all of the validation (really only about 6 types) in to individual objects that I can use across multiple classes. What I want to do, is provide parameters to the validation classes, without having to create an attribute for each validation object variation.
My attribute looks like this:
[AttributeUsage(AttributeTargets.Property)]
public class ValidationRuleAttribute : Attribute
{
public ValidationRuleAttribute(Type validationRule, string customFailureMessage = "")
{
if (typeof(IValidationRule).IsAssignableFrom(validationRule))
{
this.ValidationRule = string.IsNullOrEmpty(customFailureMessage)
? Activator.CreateInstance(validationRule, customFailureMessage) as IValidationRule
: Activator.CreateInstance(validationRule) as IValidationRule;
}
else
{
throw new ArgumentException(
string.Format(
"ValidationRule attributes can only be used with IValidationRule implementations. The '{0}' Tyoe is not supported.",
validationRule.Name));
}
}
public IValidationRule ValidationRule { get; private set; }
}
As an example, I have a simple StringIsNotNull validation object. I want to expand on it by allowing me to specify a minimum string length requirement. So the StringIsNotEmptyValidation would become StringHasMinimumLengthValidation
public class StringIsNotEmptyValidation : IValidationRule
{
private readonly string customErrorMessage;
public StringIsNotEmptyValidation()
{
}
public StringIsNotEmptyValidation(string customErrorMessage)
{
this.customErrorMessage = customErrorMessage;
}
public string ResultMessage { get; private set; }
public IValidationMessage Validate(System.Reflection.PropertyInfo property, IValidatable sender)
{
string value = property.GetValue(sender).ToString();
// Validate
bool isFailed = string.IsNullOrWhiteSpace(value);
if (isFailed)
{
if (string.IsNullOrEmpty(this.customErrorMessage))
{
DisplayNameAttribute displayName = property.GetCustomAttribute<DisplayNameAttribute>(true);
string errorMessage = displayName == null
? string.Format("You can not leave {0} empty.", property.Name)
: string.Format("You can not leave {0} empty.", displayName.DisplayName);
this.ResultMessage = errorMessage;
return new ValidationErrorMessage(errorMessage);
}
else
{
this.ResultMessage = this.customErrorMessage;
return new ValidationErrorMessage(customErrorMessage);
}
}
this.ResultMessage = string.Empty;
return null;
}
}
Within my model, I decorate my property with the attribute and validation object.
[RepositoryParameter(DbType.String)]
[ValidationRule(typeof(StringIsNotEmptyValidation))]
public string WorkDescription
{
get
{
return this.workDescription ?? string.Empty;
}
set
{
this.SetPropertyByReference(ref this.workDescription, value);
if (this.HasValidationMessageType<ValidationErrorMessage>(this.GetPropertyName(p => p.WorkDescription)))
{
this.Validate();
}
}
}
What I want to do, is write my attribute usage like this:
[ValidationRule(new StringIsNotEmptyValidation(minimumLength: 4))]
Since you can't instance objects in an attribute constructor, I'm forced to provide the attributes in my attribute constructor like this:
[ValidationRule(typeof(StringIsNotEmptyValidation), minLength: 4)]
I don't like this because if I have a ObjectIsNotNull or a StringIsInRange I will need to do two things:
Create a new attribute for each parameter variation (or a lot of overloads)
Set up the validation rule instances within the constructor, which will have varying property names.
The Validation object implements the following interface
public interface IValidationRule
{
string ResultMessage { get; }
IValidationMessage Validate(PropertyInfo property, IValidatable sender);
}
I don't want to bloat my interface with a large number of properties that might be used or might not be used depending on the Rule implementing it. It also makes it difficult to assign attribute params to the rule object.
So my question is how can I provide parameters to the IValidationRule concrete classes, without creating multiple attribute types to facilitate this? This is being used so that I an do cross-object validation. The PropertyInfo passed in to the validation rule is from a cache of PropertyInfo's. I need to keep the amount of reflection used down, otherwise I'd just use attributes for each rule parameter and use reflection on sender to figure out what ranges to use.
Update
After discussing this with Corey, it does indeed appear that attributes are supported in Universal Apps and it is only the DataAnnotations namespace that is missing. In order to get access to the attributes, I had to add a using statement to System.Reflection in order to gain access to a series of extension methods that expose the GetCustomAttribute methods. They are now extension methods and not built in to the Type class.
So I suppose in the end, I can just create my validation logic within the attributes, instead of individual objects. I can't think of any downsides to going this route.
In order to access the attributes in a Universal App, you have to include System.Reflection as a using statement, then access via the GetRuntimeProperties() extension method.
var validationRule = this
.GetType()
.GetRuntimeProperties() // Can be GetRuntimeFields or GetRuntimeMethods as well.
.FirstOrDefault(p => p.GetCustomAttribute<IntegerInRangeAttribute>() != null);
So there are a few options here.
First, and often used, is to have a different attribute for each type of rule you want to process. You are already building classes for each of your rules, so instead of having some encapsulating attribute that instantiates them all just make each rule an attribute:
[StringMinLengthRule(5)]
public string SomeString { get; set; }
Build the validation logic into your attributes - say with a base attribute that does the bulk of the work, calling a virtual method to do the actual validation. Then you can just enumerate the rule attributes and call them from your validation method.
Next, you can have a number of different properties on your attribute that can be set during declaration to provide the properties for your various rules:
[Validation(RuleType.StringMinLength, MinLength = 5)]
public string SomeString { get; set; }
You could still have the rules be processed in the ValidationAttribute itself, or create IValidationRule instances at run-time to process the actual validations. Unfortunately there's nothing to stop you from adding a Validation attribute that sets the wrong properties for the rule type, resulting in errors at run-time when you try to validate an instance.
Finally, something that works but probably shouldn't... and it's kinda ugly:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class ValidationRuleAttribute : Attribute
{
public IValidationRule ValidationRule { get; private set; }
public ValidationRuleAttribute(RuleType type, params object[] parms)
{
if (type == RuleType.NotNull)
{
if (parms.Length != 0)
throw new ArgumentException("RuleType.NotNull requires 0 parameters", "parms");
ValidationRule = new NotNullValidation();
}
if (type == RuleType.StringMinLength)
{
if (parms.Length != 1)
throw new ArgumentException("RuleType.StringMinLength requires 1 parameter", "parms");
if (!(parms[0] is int))
throw new ArgumentException("RuleType.StringMinLength requires an integer", "parms");
ValidationRule = new StringLengthValidation((int)parms[0]);
}
}
}
The biggest problem with it is that it won't complain until you try to instantiate a class at run-time that has a bad Validation attribute. Your code can run quite happily up until the point where it tries to create an instance of that bad class, at which point all of the attributes will actually be constructed and those ArgumentExceptions start flying.
In fact only the first option doesn't suffer from run-time problems, because you can control the types of parameters being supplied by using the correct constructor formats. You can still tell it to do silly things - like requiring that strings must have less than 0 length for instance - but that's up to you :P
I have a C# class that is used in my custom DB ORM tools, called DbFieldAttribute.
I place it over my field, like so:
[DbField("User_Id")]
public int UserId{ get; set; }
Challenge: From my attributes Constructor code, get the FieldInfo of the field it is associated with the attribute. In the case above, it would give me the FieldInfo for UserId.
Any help would be great. Thanks.
Unfortunately, to the best of my knowledge, there is no way to accomplish what you are asking for.
But if it is not necessary that you get the PropertyInfo or the FieldInfo object inside your constructor, but instead you would be satisfied with it being passed to a method, then there is a possible solution.
First of all, your DbField class would need to be defined in the following way.
class DbField : Attribute
{
public DbField(string source) { }
public void GetInstance(PropertyInfo source)
{
Console.WriteLine(source.Name);
}
}
You would then need to define the following class which would get all the (in this case) properties marked with the DbField attribute, and pass them to the GetInstance(PropertyInfo) method.
class ActivateAttributes
{
public ActivateAttributes(object source)
{
source.GetType()
.GetProperties()
.Where(x => x.GetCustomAttributes().OfType<DbField>().Any())
.ToList()
.ForEach(x => (x.GetCustomAttributes().OfType<DbField>().First() as DbField).GetInstance(x));
}
}
The way you would trigger this process is inside an abstract class, which is defined as so.
abstract class AbstractDecoratedClass
{
public AbstractDecoratedClass()
{
new ActivateAttributes(this);
}
}
Now your target class, which has its properties decorated by DbField attributes, simply needs to derive from this class, so that you won't be bothered by the invocation inside the constructor.
class DecoratedClass : AbstractDecoratedClass
{
[DbField("User_Id")]
public int UserId { get; set; }
[DbField("User_Id2")]
public int UserId2 { get; set; }
}
You are now only left with testing the solution as shown here.
class Program
{
static void Main()
{
new DecoratedClass();
Console.Read();
}
}
The solution could not be solved directly, as #Mario pointed out, but here is the solution I ended up going with.
The key is to know that the attribute alone has no way of knowing this information, but at the time it is called it is reasonable to expect that the FieldInfo or PropertyInfo was also available.
My original problem was that my ORM code looked to an attribute to determine if a field/property related to a database field. Then, I had instances where the Prop/Field name in the class did not match up with the database for reasons of making it more logical to the Code/Db. In those cases I needed to pass in a field name to use instead of the actual field. I was hoping the attribute could do more of the work, or at least help make it more obvious for any future code that used it.
(I stripped out xml comments and extra code not relavant to this solution)
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class DbFieldAttribute : Attribute
{
private string fieldName = "";
public DbFieldAttribute() { }
public DbFieldAttribute(string fieldName)
{
this.fieldName = fieldName;
}
public string FieldName(PropertyInfo pi)
{
if (this.fieldName != "") return this.fieldName;
else return pi.Name;
}
public string FieldName(FieldInfo fi)
{
if (this.fieldName != "") return this.fieldName;
else return fi.Name;
}
Now when my ORM code wants the field name, it has to pass in the field or property info related to that field. This means that what is needed, is now intrinsic in the attributes use, instead of needing to be derived in external code.
I am creating a network chat client in C# as a side project. In addition to simple text messages, I also have slash-prefixed commands that can be entered into the input TextBox. I used a modular approach by creating an enum that contains all the various commands, and then decorating those commands with attributes.
The attributes specify what slash-prefixed command can be entered to trigger the command, as well as any aliases to the primary command identifier and the command's usage.
Example:
public enum CommandType : byte
{
[PrimaryIdentifier("file"),
AdditionalIdentifier("f"),
CommandUsage("[<recipient>] [<filelocation>]")]
FileTransferInitiation,
[PrimaryIdentifier("accept"),
AdditionalIdentifier("a")]
AcceptFileTransfer,
// ...
}
My problem arises when I try to allow multiple aliases to the primary command. I have attempted this two ways: by allowing duplicates of the AdditionalIdentifier attribute, or by making the constructor argument in AdditionalIdentifier a params string[].
With the former, I implemented it by decorating the attribute class with AttributeUsage and setting AllowMultiple to true. While this does indeed achieve what I'm looking for, I'm feeling like it could get really noisy really fast to have several lines of aliases, in addition to the other attributes.
The latter also works, however, it generates the compiler warning CS3016, and says that that approach is not CLS-compliant. Obviously, this doesn't necessarily stop me from still using it, but I've learned to always treat warnings as errors.
My actual question is should I ignore my objections with duplicates and just go ahead and use them, or is there some other solution that could be used?
Thank you.
You could also use "params string[] aliases" in the constructor to allow a variable argument list:
[AttributeUsage(AttributeTargets.Method)]
class TestAttribute : Attribute
{
public TestAttribute(params string[] aliases)
{
allowedAliases = aliases;
}
public string[] allowedAliases { get; set; }
}
This would allow you to do:
[Test("test1", "test2", "test3")]
static void Main(string[] args)
Personally I would go with the AllowMultiple approach: I don't think the "noise" is going to be that much of a problem unless you really have truckloads of identifiers for each command. But if you don't like that and want to stay CLS-compliant, one other solution would be to provide overloaded constructors for AdditionalIdentifierAttribute:
public AdditionalIdentifierAttribute(string id) { ... }
public AdditionalIdentifierAttribute(string id1, string id2) { ... }
public AdditionalIdentifierAttribute(string id1, string id2, string id3) { ... }
The downside is that this does limit you to a predetermined number of identifiers.
That said, CLS compliance is really only a major consideration if you are building a library that others are likely to use (and specifically from other languages). If this type or the library is internal to your application, then it's reasonable to ignore CLS compliance warnings.
EDIT: Thinking further about this, you have quite a lot of attributes on those enums. You might want to consider creating an abstract Command class instead, and exposing the identifiers, usage, etc. as properties of that class; then derive concrete types of Command which return the appropriate values from those properties. This potentially also allows you to move the handling logic into those Command objects rather than switching on the enum value.
Why not have a single attribute with multiple properties? Have the property for the alias take a comma-separated list. This is the approach they take in MVC for things like the AuthorizeAttribute for Roles. Internally, the property parses the string into an array for ease of use in the attribute class, but it allows you an easy way to set up your configuration.
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();
}
}
}
Then use it like:
public enum CommandType : byte
{
[Identifer( Name = "file", Aliases = "f", Usage = "..." )]
FileTransferType,
...
}
Yet another approach would be to have the attribute take an array of strings as a constructor parameter - that way, you get the compiler to parse the array for you (at the expense of a little more goop when applying the attribute) thus:
[Identifiers(new string[] {"Bill", "Ben", "Ted"})]
A quick 'n dirty example of implementing & using such a technique looks like this:
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);
}
}
}
}