Using special character in Enum e.g. % (C#3.0) - c#

I ahve a combo whose source is an Enum. Now , among the other values(say value1, value2
etc.) there is one item Changes(%) that will be displayed in the combo .
How to define Changes(%) in the enum?
Using C#3.0
Thanks

You can't. Enum value names have to be valid C# identifiers. You shouldn't be trying to put display names in there.
Instead, consider decorating each value with a [Description] attribute which you can fetch with reflection:
public enum ChangeType
{
[Description("Changes (%)")]
PercentageChanges,
[Description("Changes (absolute)")]
AbsoluteChanges
}
Alternatively define resources, possibly using the enum value name as the resource key. This will be better for i18n purposes, apart from anything else, although more work if you don't need your app to be internationalized.
EDIT: Here's a WayBack machine archive of an article going into more detail.

C# enumerations compile out as sealed classes, inheriting from Enum, with public static fields carrying the name of your enumeration members, thus, you're asking the compiler to name fields things like "<", ">" and "=", and that's not accepted.
Enumeration values carry the same restrictions as properties and fields when it comes to naming.

FWIW ... this article goes in-depth about the exact methodology of achieving what Jon Skeet suggests >>>> ... Pulling Enumeration Descriptions From A Resource File

I use Jon's approach (a Description attribute against the enumerations) along with the extension method shown below to make it convenient to retrieve the description:
public static class EnumExtensions
{
public static T GetAttribute<T>(this Enum enumerationValue) where T : Attribute
{
T[] attributes = GetAttributes<T>(enumerationValue);
return attributes.Length > 0 ? attributes[0] : null;
}
public static string GetDescription(this Enum enumerationValue, string descriptionIfNull = "")
{
if (enumerationValue != null)
{
DescriptionAttribute attribute = enumerationValue.GetAttribute<DescriptionAttribute>();
return attribute != null ? attribute.Description : enumerationValue.ToString();
}
return descriptionIfNull;
}
}
Example of usage:
[TestClass]
public class WhenGettingDescriptionOfAnEnum
{
private enum SampleEnum
{
First,
[Description("description")]
Second
}
[TestMethod]
public void ShouldReturnNameOfEnumIfItHasNoDescription()
{
SampleEnum.First.GetDescription().Should().Be("First");
}
[TestMethod]
public void ShouldReturnDescriptionIfThereIsOne()
{
SampleEnum.Second.GetDescription().Should().Be("description");
}
}

Related

How to decorate enum with attribute in c#

I have an enum on helper library in my solution.
For example
public enum MyEnum
{
First,
Second
}
I want to use MyEnum in a few another project. I want to decorate this enum in each project with own attribute like this:
public enum MyEnum
{
[MyAttribute(param)]
First,
[MyAttribute(param2)]
Second
}
How to decorate enum from another library with own local attribute?
You can't do what you've described - the best you can do is to create a new Enum that uses the same set of values. You will then need to cast to the "real" enum whenever you use it.
You could use T4 templates or similar to generate the attributed enum for you - it would be much safer that way as it would be very easy to map the wrong values, making for some very subtle bugs!
Linqpad Query
enum PrimaryColor
{
Red,
Blue,
Green
}
enum AttributedPrimaryColor
{
[MyAttribute]
Red = PrimaryColor.Red,
[MyAttribute]
Blue = PrimaryColor.Blue,
[MyAttribute]
Green = PrimaryColor.Green
}
static void PrintColor(PrimaryColor color)
{
Console.WriteLine(color);
}
void Main()
{
// We have to perform a cast to PrimaryColor here.
// As they both have the same base type (int in this case)
// this cast will be fine.
PrintColor((PrimaryColor)AttributedPrimaryColor.Red);
}
Attributes are compile-time additions (metadata) to code. You can not modify them when using the compiled code assembly.
(Or perhaps you could if you are a diehard low-level IL wizard, but I certainly am not...)
If your enum values require modification or parameters at various places, then you should consider other solutions, e.g. a Dictionary or even a Database Table.
E.g. using a Dictionary:
var values = new Dictionary<MyEnum, int>()
{
{ MyEnum.First, 25 },
{ MyEnum.Second, 42 }
};
var valueForSecond = values[MyEnum.Second]; // returns 42
You can do something like this, but it will be tedious.
The idea is to use your project settings to allow the change when you import the enum in a new project.
First, you will need 2 attributes:
// This one is to indicate the format of the keys in your settings
public class EnumAttribute : Attribute
{
public EnumAttribute(string key)
{
Key = key;
}
public string Key { get; }
}
// This one is to give an id to your enum field
[AttributeUsage(AttributeTargets.Field)]
public class EnumValueAttribute : Attribute
{
public EnumValueAttribute(int id)
{
Id = id;
}
public int Id { get; }
}
Then, this method:
// This method will get your attribute value from your enum value
public object GetEnumAttributeValue<TEnum>(TEnum value)
{
var enumAttribute = (EnumAttribute)typeof(TEnum)
.GetCustomAttributes(typeof(EnumAttribute), false)
.First();
var valueAttribute = (EnumValueAttribute)typeof(TEnum).GetMember(value.ToString())
.First()
.GetCustomAttributes(typeof(EnumValueAttribute), false)
.First();
return Settings.Default[String.Format(enumAttribute.Key, valueAttribute.Id)];
}
I did not check if the type is correct, not even if it finds any attributes. You will have to do it, otherwise you will get an exception if you don't provide the right type.
Now, your enum will look like that:
[Enum("Key{0}")]
public enum MyEnum
{
[EnumValue(0)] First,
[EnumValue(1)] Second
}
Finally, in your project settings, you will have to add as many lines as the number of members in your enum.
You will have to name each line with the same pattern as the parameter given to EnumAttribute. Here, it's "Key{0}", so:
Key0: Your first value
Key1: Your second value
etc...
Like this, you only have to change your settings values (NOT THE KEY) to import your enum and change your attributes to a project to another.
Usage:
/*Wherever you put your method*/.GetEnumAttributeValue(MyEnum.First);
It will return you "Your first value".

C# Dash (-) in String Enum

I'm creating an enum for a field in a Class and I need one of the values to be Start-up. However, when I type this, I get an error } expected. StartUp, on the other hand, is allowed but I really need the dash to be in there..
I also have other fields with the same problem for spaces..
Does anyone know of a way that I can get the value as shown above?
Code Example:
using System;
namespace XMLConverter.Models
{
public enum SiteStatus { Hold, Planned, Proposed, Qualifying, StartUp, Open, Enrollment Complete, Closed, Stopped, Not Selected, Withdrew }
public class Site
{
public string StudyName { get; set; }
public string SiteNumber { get; set; }
public string SiteName { get; set; }
public SiteStatus SiteStatus { get; set; }
}
}
You can't. Identifiers in C# don't allow dashes since it is an operator, so that is the same for enum values.
There is no way around this. I would suggest to replace that for another sign, like an underscore (_). Depending on the use of the enum, you might have luck with your serializer. If you for example use JSON, there are possibilities to serialize and deserialize a value differently.
Enum members are just like every other identifier, and as such their names need to satisfy a set of rules. Part of these rules means that you cannot have dashes in identifier or enum names names.
And if you think about it practically, it does not make a lot of sense. Just imagine what would happen if Start and up are two variables. Since C# ignores whitespace in expressions, Start-up would mean Start - up which is a subtraction operation.
While the enum member itself needs to be a valid identifier, you can control how it is rendered to other parties. For example, you can use the DisplayAttribute to affect how it is rendered on UIs, and you can the DataMemberAttribute to affect how it is serialized.
An identifier in c# cannot contain the dash character, so you can't do this.
If you need to obtain a description for an enum value, you could use the [Description] attribute, For example:
enum TestEnum
{
[Description("Start-up")]
StartUp
}
TestEnum val = TestEnum.StartUp;
string desc = GetEnumDescription((TestEnum)val);
Where the method GetEnumDescription looks like this: (source: How to get C# Enum description from value?)
public static string GetEnumDescription(Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute),
false);
if (attributes != null &&
attributes.Length > 0)
return attributes[0].Description;
else
return value.ToString();
}
There is no reason to be putting "-" in the enum values.
1.If you want to display it that way to your applications users you can create an attribute and check the value of the attribute when displaying the enum value.
2.as #patric pointed out replace it with a character and check the existense of the character when displaying the value and replace it with '-'
At the first look, it's weird, but nowadays in microservices, some other languages (like GoLang) use 'dash' in the enum name, and it is okay for them, but in c# we have to add an attribute for them to handle this problem.
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
public class Program
{
public static void Main()
{
var toolViewMode = ToolViewModeType.Extended;
Console.WriteLine(toolViewMode.GetEnumMemberValue());
}
}
public static class Extensions {
public static string GetEnumMemberValue<T>(this T value)
where T : struct, IConvertible
{
return typeof(T)
.GetTypeInfo()
.DeclaredMembers
.SingleOrDefault(x => x.Name == value.ToString())
?.GetCustomAttribute<EnumMemberAttribute>(false)
?.Value;
}
}
public enum ToolViewModeType
{
[EnumMember(Value = "basic-view")]
Basic,
[EnumMember(Value = "extended-view")]
Extended
}

CA1019: Define accessor for attribute argument. I don't understand the reason

Today, I was cleaning up some of my code with FXCop and it complained about a Attribute class I had with this violation.
CA1019: Define accessor for attribute argument.
On this page, http://msdn.microsoft.com/en-us/library/ms182136.aspx there is more information, but I still do not get the reason for this as it seems to me more verbose and less relevant.
It gives two codes samples.
using System;
namespace DesignLibrary
{
// Violates rule: DefineAccessorsForAttributeArguments.
[AttributeUsage(AttributeTargets.All)]
public sealed class BadCustomAttribute :Attribute
{
string data;
// Missing the property that corresponds to
// the someStringData parameter.
public BadCustomAttribute(string someStringData)
{
data = someStringData;
}
}
// Satisfies rule: Attributes should have accessors for all arguments.
[AttributeUsage(AttributeTargets.All)]
public sealed class GoodCustomAttribute :Attribute
{
string data;
public GoodCustomAttribute(string someStringData)
{
data = someStringData;
}
//The constructor parameter and property
//name are the same except for case.
public string SomeStringData
{
get
{
return data;
}
}
}
}
I don't understand why the SomeStringData property is required. Isn't the someStringData a parameter? Why does it need to have its own property if it is already stored in another property?
Actually, mine is a little different as it looks like this.
[AttributeUsage(AttributeTargets.Property)]
public sealed class ExampleAttribute : Attribute
{
public ExampleAttribute(string attributeValue)
{
this.Path = attributeValue;
}
public string Name
{
get;
set;
}
// Add to add this to stop the CA1019 moaning but I find it useless and stupid?
public string AttributeValue
{
get
{
return this.Name;
}
}
}
Rather than a private field, I have used a public autoproperty, I had to add the last part to make the warning stop but I don't see the point and it also adds another public field to this class, which is redundant, and seems less clean.
That said, I assume that this warning is raised for a reason so what good reason I am missing here?
Thanks in advance.
FxCop is complaining because your existing property doesn't match the parameter name.
Therefore, it doesn't realize that the parameter actually is exposed.
You should rename the property or parameter to match (except for case), or suppress the warning.
FxCop rule CA1019 is just enforcing the .Net Framework coding guidelines for Attributes.
Use named arguments (read/write properties) for optional parameters. Provide a read/write property with the same name as each named argument, but change the case to differentiate between them.
Documentation Link: http://msdn.microsoft.com/en-us/library/2ab31zeh(v=vs.71).aspx
The reason behind the FxCop warning is that every piece of data you pass into the attribute's constructor should be made publicly available to access when the attribute instance is being retrieved by Reflection.
Let's say you have this:
[BadCustom("My String Data")]
public class DecoratedClass
{
}
How will you get "My String Data" back from that attribute instance when you read it using:
BadCustomAttribute attr = typeof(DecoratedClass)
.GetCustomAttributes(typeof(BadCustomAttribute), false)
.Single() as BadCustomAttribute;
Now you have the instance of your attribute, but no way to read the string passed into the constructor because you didn't at least declare a read-only property for it.
the idea is that you should write just:
[AttributeUsage(AttributeTargets.Property)]
public sealed class ExampleAttribute : Attribute
{
public ExampleAttribute(string attributeValue)
{
this.AttributeValue = attributeValue;
}
public string AttributeValue
{
get;
set;
}
}
This violation will also be thrown when the parameter name matches the property name, but the data types are different.

How to name enums versus properties

In the following contrived C# code, I don't like that an enum name is the same as the property name.
public enum CollarType
{
Classic,
VNeck
}
public class Shirt
{
public CollarType CollarType { get ... }
}
In the old days when more people were using hungarian / other bizarre naming like class CShirt, this kind of conflict didn't happen. But today I run into it constantly.
How do you handle this situation? Do you just live with the fact that so many things have the same name, or do you have a better naming scheme?
The only limitation of having the enum name be the same as the property name is that you cannot define the enum as nested type of the class with the property.
E.g. this does not work:
public class Shirt
{
public enum CollarType
{
Classic,
VNeck
}
public CollarType CollarType { get ... }
}
However, on the positive side, it does make the code more readable IMHO because it clearly< shows the association between the enum value and the property:
myShirt.CollarType = CollarType.Classic;
Yes, it's absolutely normal to have a property with the same name as it's type name.
The convention in the BCL is just to run with it. With the exception of nested enums, it doesn't cause any ambiguities.

C# Attributes - Arrays or Duplicates?

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);
}
}
}
}

Categories