I have a list of strings which are candidates for Enumerations values. They are
Don't send diffs
500 lines
1000 lines
5000 lines
Send entire diff
The problem is that spaces, special characters are not a part of identifiers and even cannot start with a number, so I would be sanitizing these values to only chars, numbers and _
To keep the original values I thought of putting these strings in the DescriptionAttribute, such that the final Enum should look like
public enum DiffBehvaiour
{
[Description("Don't send diffs")]
Dont_send_diffs,
[Description("500 lines")]
Diff_500_lines,
[Description("1000 lines")]
Diff_1000_lines,
[Description("5000 lines")]
Diff_5000_lines,
[Description("Send entire diff")]
Send_entire_diff
}
Then later using code I will retrieve the real string associated with the enumeration value, so that the correct string can be sent back the web service to get the correct resource.
I want to know how to create the DescriptionAttribute using System.Reflection.Emit
Basically the question is where and how to store the original string so that when the Enumeration value is chosen, the corresponding value can be retrieved.
I am also interested in knowing how to access DescriptionAttribute when needed.
Ok, if you really want to use reflection:
DiffBehvaiour value = DiffBehvaiour.Dont_send_diffs;
FieldInfo enumField = value.GetType().GetField(value.ToString());
DescriptionAttribute attribute = (DescriptionAttribute)enumField.
GetCustomAttributes(typeof(DescriptionAttribute), true)[0];
Console.WriteLine(attribute.Description);
$> Don't send diffs
Obviously there is no error handling, etc, but the basic idea is there.
Update
I now think I see the point of your question, which myself and the other people that answered actually missed.
You want to decorate an enum with attributes at runtime i.e. add attributes to a type at runtime. Adding attributes to a type at runtime is not possible.
However these is support in the .Net for a type metadata engine via : TypeDescritor:
MSDN http://msdn.microsoft.com/en-us/library/system.componentmodel.typedescriptor.aspx
Example http://geekswithblogs.net/abhijeetp/archive/2009/01/10/dynamic-attributes-in-c.aspx
The TypeDescritor framework allows you to dynamically provide type information rather than actually dynamically decorating types directly - it is a layer of indirection.
You may be able to bend this mechanism to support what you want to do, but at the end of the day you will need to maintain a lookup for your enum members to provide the description strings. Using a lookup structure to maintain a mapping between your enum members and description string was my first answer and the first answer to this question...
You could write a generic method like this:
class EnumExtensions
{
public static string GetDescription<TEnum>(TEnum value)
// inexpressible generic constraint TEnum : System.Enum
{
// reflection lookup of this value per #chibacity answer
}
public static IDictionary<TEnum,string> GetDescriptions<TEnum>()
// inexpressible generic constraint TEnum : System.Enum
{
// do the reflection lookups once and build a dictionary
var result = new Dictionary<TEnum, string>();
foreach(string name in Enum.GetNames(typeof(TEnum))
{
var value = (TEnum)Enum.Parse(typeof(TEnum), name);
var description = GetDescription(value);
result.Add(value, description);
}
return result;
}
}
Related
After perusing some other questions regarding common ways to generically create access to friendly strings for enumeration values in C# this answer appeared to be my best bet for a generic solution where the friendly strings can be placed in the definition of the enumeration using DescriptionAttributes.
I implemented this as an extension method, but quickly realized that it would only work for standard enums, where the [Flags] attribute is not specified. I'm not completely sure of the best way to pursue implementing this for cases where the attribute is present.
Since the flags attribute means that multiple values can be selected simultaneously, using a single "friendly string" would not make sense. I was thinking of defining the friendly strings in the same way, but overloading the extension method to take that specific enum type where it would return a List<string> to provide friendly strings for all of the selected values.
The solution described above would work, but I feel like there will be lots of code duplication since each enum that uses the Flags attribute will require it's own extension method because enums can only be inherited by System.Enum, eliminating my ability to create a base type. It would be nicer if I could have a more generic method that can handle this by checking the enum if the flags attribute is present and then return one of the following:
single string (if no Flags), list (if Flags) - signature returns object
single string (if no Flags), list (if Flags) - signature returns dynamic
list that may only contain one value if flags is not specified - signature returns List<string>
I feel like this question may be a case of "having my cake and eating it too", since I would prefer to not have to do additional checks after getting the friendly string(s) and deduping my code. Is there a trick or good way to do this that isn't a messy hack?
You can write a method just as in the answer you link, but with support for flag enums, and return a comma seperated string, something like:
public static string GetDescription(Enum value)
{
Type type = value.GetType();
var values = Enum.GetValues(type);
var setValues = new List<Enum>();
foreach(var enumValue in values)
{
if (value.HasFlag((Enum)enumValue))
setValues.Add((Enum)enumValue);
}
var stringList = new List<string>();
foreach (var singleValue in setValues)
{
var name = Enum.GetName(type, singleValue);
if (name != null)
{
FieldInfo field = type.GetField(name);
if (field != null)
{
DescriptionAttribute attr =
Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr != null)
{
stringList.Add(attr.Description);
}
}
}
}
return string.Join(",", stringList.ToArray());
}
not the cleanest code, but you get the idea, only keep in my mind, that it wont work as expected for enums that are not flags - just throwing an idea.
Use enum.ToString() to get "unfriendly" strings (where enum is your Enum variable). Write a reusable extension method to convert UnfriendlyString to a friendly "unfriendly string" (eg insert space-lowercase wherever there is an uppercase or something similar).
For [Flags] you could either Split the unfriendly string, convert each sub-string, and perhaps Join again; or your extension method could take the commas into account.
I've seen many questions and answers about mapping strings to enums and vice-versa, but how can I map a series of localized strings to enums?
Should I just create an extension method like this that returns the proper string from a resource file? Is there a way to localize attributes (like "Description") that are used in solutions like this?
Which is the preferred solution - extension method or attributes. It seems to me that this isn't the intended purpose of attributes. In fact, now that I think about it, if I were to use an extension method an attribute seems like something I'd use to specify a key in a resource file for the localized string I want to use in place of the enum value.
EDIT - example:
Given the following enum,
public enum TransactionTypes {
Cheque = 1,
BankTransfer = 2,
CreditCard = 3
}
I would like a way to map each type to a localized string. I started off with an extension method for the enum that uses a switch statement and strongly typed references to the resource file.
However, an extension method for every enum doesn't seem like a great solution. I've started following this to create a custom attribute for each enumerated value. The attribute has a base name and key for the resource file containing localized strings. In the above enum, for example, I have this:
...
[EnumResourceAttribute("FinancialTransaction", "Cheque")]
Cheque = 1,
...
Where "FinanacialTransaction" is the resx file and "Cheque" is the string key. I'm trying to create a utility method to which I could pass any value from any enumeration and have it return the localized string representation of that value, assuming the custom attribute is specified. I just can't figure out how to dynamically access a resource file and a key within it.
I would definitely suggest using a resource file, probably with a method (extension or otherwise) to make it simple to get hold of the relevant resource. As the number of languages you support grows, you don't really want the code to be full of text, distracting you from the values themselves.
Likewise translation companies are likely to be geared up to handle resx files - they're not going to want to mess around in your source code, and you shouldn't let them do so anyway :)
Just use resources which are keyed on the name of the enum and the value within it. Straightforward, scales to multiple enums and multiple languages, doesn't clutter up your source code, works well with translation tools, and is basically going along with the flow of i18n within .NET.
EDIT: For mapping the enum values to the resource names, I'd just do something like:
public static string ToResourceName<T>(this T value) where T : struct
{
return typeof(T).Name + "." + value;
}
Then you could do:
string resource = MyEnum.SomeValue.ToResourceName();
Obviously that's performing string concatenation every time - you could cache that if you wanted to, but I wouldn't bother unless you had some indication that it was actually a problem.
That doesn't stop you using the extension method for non-enums, of course. If you want to do that, you need something like Unconstrained Melody.
I continued with the custom attributes and created this utility method:
public static string getEnumResourceString(Enum value)
{
System.Reflection.FieldInfo fi = value.GetType().GetField(value.ToString());
EnumResourceAttribute attr = (EnumResourceAttribute)System.Attribute.GetCustomAttribute(fi, typeof(EnumResourceAttribute));
return (string)HttpContext.GetGlobalResourceObject(attr.BaseName, attr.Key);
}
I believe there is no human way to change any attribute or field inside an Attribute apart from doing it in the constructor. That is, short of redesigning and recompiling Visual Studio yourself. There is already a similar question posted here:
Change Attribute's parameter at runtime
but I believe the peculiarities of my problem are different enough to require a new post.
I use an enumeration to keep track of the different columns of a DataTable. I use attributes in each enumeration element to indicate the underlying type and the description -in case the .ToString() would give an "ugly" result due to the rigid set of characters that are allowed to name an enumeration element, such as "Tomato_Field" when you want "Tomato Field", and the like. This allows me to place all the related information in the same object, which is, I believe, what it should be. This way I can later create all the columns with a simple and clean foreach that cycles through the elements of the enumeration and extracts the metedata (description and type) to create each column.
Now, some of the columns are autocalculated, which means that during their creation -via DataTable Identifier.Columns.Add.(NameOfColumn,underlyingType,optional: autocalculatedString)- I need to specify a string that determines how it should be calculated. That string must use the names of other columns, which might be in the Description Attribute. The approach that looks logical is to use another attribute that holds the string, which should be built using the names of the other columns, requiring access to the metadata. Now that seems impossible in the constructor: you are forced to provide a constant string. You can't use a method or anything.
This problem could be solved if there were a way to change a property inside the attribute (lets call it AutocalculatedStringAttribute) at runtime. If you access the metadata you can retrieve the string you used at the constructor of the Attribute, and you can of course change that string. However, if you later access the metadata again that change is ignored, I believe the constructor is called every time the metadata is accessed at runtime, thus ignoring any changes.
There are, of course, dirty ways to achive what I am trying to do, but my question is specifically if there is a way to properly use attributes for this. Short of resorting to CodeDOM to recompile the whole assembly with the constructor of the AutocalculatedStringAttribute changed, a certain overkill.
Right, the metadata that's used to initialize the attribute is immutable. But you can add properties and methods to an attribute class that can run code and return relevant info after the attribute object is constructed. The data they rely on doesn't have to be stored in metadata, it can be persisted anywhere.
Of course, such code wouldn't have to be part of the attribute class implementation, it could just as well be part of the code that instantiates the attribute. Which is where it belongs.
It isn't entirely clear to me what code is consuming this attribute, and it matters...
You cannot change an attribute that is burned into the code - you can query it with reflection, but that is about it. However, in many cases you can still do interesting things - I don't know if they apply to your scenario, though:
you can subclass many attributes like [Description], [DisplayName], etc - and while you pass in a constant string (typically a key) to the .ctor, it can return (through regular C#) more flexible values - perhaps looking up the description from a resx to implement i18n
if the caller respects System.ComponentModel, you can attach attributes at runtime to types etc very easily - but much harder on individual properties, especially in the case of DataTable etc (since that has a custom descriptor model via DataView)
you can wrap things and provide your own model via ICustomTypeDescriptor / TypeDescriptionProvider / PropertyDescriptor - lots of work, but provides access to set your own attributes, or return a description (etc) outside of attributes
I don't know how much of this is suitable for your environment (perhaps show some code of what you have and what you want), but it highlights that (re the question title) yes: there are things you can do to tweak how attributes are perceived at runtime.
I wanted to post this as a comment but since I wanted to include some code I couldn't, given the 600 characters limit. This is the cleanest solution I have managed to find, although it does not include all the info to create the columns on the enum, which is my goal. I have translated every field to make it easier to follow. I am not showing some code which has an obvious use (in particular the implementations of the other custom attributes and their static methods to retrieve the metadata, assume that it works).
This gets the job done, but I would ideally like to include the information stored in the strings "instancesXExpString " and "totalInstancesString" in the Autocalculated attribute, which currently only marks the columns that have such a string. This is what I have been unable to do and what, I believe, cannot be easily accomplished via subclassing -although it is an ingenious approach, I must say.
Thanks for the two prompt replies, btw.
And without any further ado, lets get to the code:
// Form in which the DataGridView, its underlying DataTable and hence the enumeration are:
public partial class MainMenu : Form {
(...)
DataTable dt_expTable;
//Enum that should have all the info on its own... but does not:
public enum e_columns {
[TypeAttribute(typeof(int))]
Experiments = 0,
[TypeAttribute(typeof(decimal))]
Probability,
[DescriptionAttribute("Samples / Exp.")]
[TypeAttribute(typeof(int))]
SamplesXExperiment,
[DescriptionAttribute("Instances / Sample")]
[TypeAttribute(typeof(int))]
InstancesXSample,
[DescriptionAttribute("Instances / Exp.")]
[TypeAttribute(typeof(int))]
[Autocalculated()]
InstancesXExp,
[DescriptionAttribute("Total Instances")]
[TypeAttribute(typeof(long))]
[Autocalculated()]
Total_Instances
};
//These are the two strings
string instancesXExpString = "[" + DescriptionAttribute.obtain(e_columns.SamplesXExperiment) + "] * [" + DescriptionAttribute.obtain(e_columns.InstancesXMuestra) + "]";
string totalInstancesString = "[" + DescriptionAttribute.obtain(e_columns.InstancesXExp) + "] * [" + DescriptionAttribute.obtain(e_columns.Experiments) + "]";
public MainMenu() {
InitializeComponent();
(...)
}
private void MainMenu_Load(object sender, EventArgs e) {
(...)
// This is the neat foreach I refered to:
foreach (e_columns en in Enum.GetValues(typeof(e_columnas))) {
addColumnDT(en);
}
}
private void addColumnDT(Enum en) {
//*This is a custom static method for a custom attrib. that simply retrieves the description string or
//the standard .ToString() if there is no such attribute.*/
string s_columnName = DescriptionAttribute.obtain(en);
bool b_typeExists;
string s_calculusString;
Type TypeAttribute = TypeAttribute.obtain(en, out b_typeExists);
if (!b_typeExists) throw (new ArgumentNullException("Type has not been defined for one of the columns."));
if (isCalculatedColumn(DescriptionAttribute.obtain(en))) {
s_calculusString = calcString(en);
dt_expTable.Columns.Add(s_columnName, TypeAttribute, s_calculusString);
} else {
dt_expTable.Columns.Add(s_columnName, TypeAttribute);
}
}
private string calcString(Enum en) {
if (en.ToString() == e_columns.InstancessXExp.ToString()) {
return instancesXExpString;
} else if (en.ToString() == e_columns.Total_Samples.ToString()) {
return totalInstancesString;
} else throw (new ArgumentException("There is a column with the autocalculated attribute whose calculus string has not been considered."));
}
(...)
}
I hope this piece of code clarifies the situation and what I am trying to do.
I've encourted a problem recently about cycling between constants of an enum class in .net (that is created from my OWL class, by Rowlex OwlGrinder). Problem was solved by means of using .net reflection (thanks to great help from dear Mr. Jon Skeet):
stackoverflow:problem-cycling-enum-class-values
By solving this, I started using it. After matching a dropDownList selected value to one of the enum class instances, I had to declare the selected object(=Language) to my RDF subject(=learningResource), via a predicate (=hasLanguage).
//learningResource is a new RDF subject, hasLanguage is predicate, and there
//is a new value for it - Language.
System.Reflection.FieldInfo[] resLanFields =
typeof(Language).GetFields();
for (int i = 0; i < resLangFields.Length; i++)
{
if (resLanFields[i].Name.Equals(dropDownList_lang.SelectedValue))
learningResource.hasLanguage = ??? //i-th constant of Language
}
Now the problem appears; I can not use Language[i] (or something like this to select i-th constant of Language class) to assign to hasLanguage. Is there a way to select i-th constant of an enum class (like refelections)?
Would any one please help me in this situation?
The Language class is not an enum in C# terminology. It is an ordinary class with public const string fields. ROWLEX intentionally generates enum-imitating-classes instead of native enums for 2 reasons:
The value of an native C# enum is an integer, while a public const string field can take the URI of the OWL class instance.
The class can have one additional public static string that is "URI" which represents the class URI consistently for every single ROWLEX generated class.
That was the background. If I understood your question right, you had an issue binding the selected name displayed in the dropdown back to the URI, and you wanted to use the position of the element inside the array you created. I would not do it that way. DropDownLists typically have both ID and Value fields for every list item (can be named differently component to component). The ID is expected to be a unique object while the Value is expected to contain something human readable. Set the ID with URI of the "enum" field, and the Value as you have done. So this is how you populate your dropdown:
System.Reflection.FieldInfo[] resLanFields = typeof(Language).GetFields();
foreach(FieldInfo field in resLanFields)
{
ListItem item = new ListItem();
item.ID = field.GetValue(null); // takes the URI value of the const field
item.Value = field.Name; // takes the name of the const field
dropDownList_lang.AddItem(item);
}
And when the user made his/her choice, this is how you read the result out:
learningResource.hasLanguage = (string) dropDownList_lang.SelectedItem.ID;
Since there is implicit casting operator implemented on the ROWLEX enum-imitating-class (string=>Language) you can safely set your triple subject as a string. Should compile without issue. Simple, isn't it?
;)
I'd say
resLanFields[i].GetValue(null)
but if this works, don't vote me up, this was in Jon's original answer.
So if I have a method of parsing a text file and returning a list of a list of key value pairs, and want to create objects from the kvps returned (each list of kvps represents a different object), what would be the best method?
The first method that pops into mind is pretty simple, just keep a list of keywords:
private const string NAME = "name";
private const string PREFIX = "prefix";
and check against the keys I get for the constants I want, defined above. This is a fairly core piece of the project I'm working on though, so I want to do it well; does anyone have any more robust suggestions (not saying there's anything inherently un-robust about the above method - I'm just asking around)?
Edit:
More details have been asked for. I'm working on a little game in my spare time, and I am building up the game world with configuration files. There are four - one defines all creatures, another defines all areas (and their locations in a map), another all objects, and a final one defines various configuration options and things that don't fit else where. With the first three configuration files, I will be creating objects based on the content of the files - it will be quite text-heavy, so there will be a lot of strings, things like names, plurals, prefixes - that sort of thing. The configuration values are all like so:
-
key: value
key: value
-
key: value
key: value
-
Where the '-' line denotes a new section/object.
Take a deep look at the XmlSerializer. Even if you are constrained to not use XML on-disk, you might want to copy some of its features. This could then look like this:
public class DataObject {
[Column("name")]
public string Name { get; set; }
[Column("prefix")]
public string Prefix { get; set; }
}
Be careful though to include some kind of format version in your files, or you will be in hell's kitchen come the next format change.
Making a lot of unwarranted assumptions, I think that the best approach would be to create a Factory that will receive the list of key value pairs and return the proper object or throw an exception if it's invalid (or create a dummy object, or whatever is better in the particular case).
private class Factory {
public static IConfigurationObject Factory(List<string> keyValuePair) {
switch (keyValuePair[0]) {
case "x":
return new x(keyValuePair[1]);
break;
/* etc. */
default:
throw new ArgumentException("Wrong parameter in the file");
}
}
}
The strongest assumption here is that all your objects can be treated partly like the same (ie, they implement the same interface (IConfigurationObject in the example) or belong to the same inheritance tree).
If they don't, then it depends on your program flow and what are you doing with them. But nonetheless, they should :)
EDIT: Given your explanation, you could have one Factory per file type, the switch in it would be the authoritative source on the allowed types per file type and they probably share something in common. Reflection is possible, but it's riskier because it's less obvious and self documenting than this one.
What do you need object for? The way you describe it, you'll use them as some kind (of key-wise) restricted map anyway. If you do not need some kind of inheritance, I'd simply wrap a map-like structure into a object like this:
[java-inspired pseudo-code:]
class RestrictedKVDataStore {
const ALLOWED_KEYS = new Collection('name', 'prefix');
Map data = new Map();
void put(String key, Object value) {
if (ALLOWED_KEYS.contains(key))
data.put(key, value)
}
Object get(String key) {
return data.get(key);
}
}
You could create an interface that matched the column names, and then use the Reflection.Emit API to create a type at runtime that gave access to the data in the fields.
EDIT:
Scratch that, this still applies, but I think what your doing is reading a configuration file and parsing it into this:
List<List<KeyValuePair<String,String>>> itemConfig =
new List<List<KeyValuePair<String,String>>>();
In this case, we can still use a reflection factory to instantiate the objects, I'd just pass in the nested inner list to it, instead of passing each individual key/value pair.
OLD POST:
Here is a clever little way to do this using reflection:
The basic idea:
Use a common base class for each Object class.
Put all of these classes in their own assembly.
Put this factory in that assembly too.
Pass in the KeyValuePair that you read from your config, and in return it finds the class that matches KV.Key and instantiates it with KV.Value
public class KeyValueToObjectFactory
{
private Dictionary _kvTypes = new Dictionary();
public KeyValueToObjectFactory()
{
// Preload the Types into a dictionary so we can look them up later
// Obviously, you want to reuse the factory to minimize overhead, so don't
// do something stupid like instantiate a new factory in a loop.
foreach (Type type in typeof(KeyValueToObjectFactory).Assembly.GetTypes())
{
if (type.IsSubclassOf(typeof(KVObjectBase)))
{
_kvTypes[type.Name.ToLower()] = type;
}
}
}
public KVObjectBase CreateObjectFromKV(KeyValuePair kv)
{
if (kv != null)
{
string kvName = kv.Key;
// If the Type information is in our Dictionary, instantiate a new instance of that class.
Type kvType;
if (_kvTypes.TryGetValue(kvName, out kvType))
{
return (KVObjectBase)Activator.CreateInstance(kvType, kv.Value);
}
else
{
throw new ArgumentException("Unrecognized KV Pair");
}
}
else
{
return null;
}
}
}
#David:
I already have the parser (and most of these will be hand written, so I decided against XML). But that looks like I really nice way of doing it; I'll have to check it out. Excellent point about versioning too.
#Argelbargel:
That looks good too. :')
...This is a fairly core piece of the
project I'm working on though...
Is it really?
It's tempting to just abstract it and provide a basic implementation with the intention of refactoring later on.
Then you can get on with what matters: the game.
Just a thought
<bb />
Is it really?
Yes; I have thought this out. Far be it from me to do more work than neccessary. :')