I am trying to refactor the mother of all switches and am not really sure how to do it. Here is the existing code:
bool FieldSave(Claim claim, string field, string value)
{
//out vars for tryparses
decimal outDec;
int outInt;
bool outBool;
DateTime outDT;
//our return value
bool FieldWasSaved = true;
//World Greatest Switch - God help us all.
switch (field)
{
case "Loan.FhaCaseNumber":
GetLoan(claim).FhaCaseNumber = value;
break;
case "Loan.FhaInsurance":
if (bool.TryParse(value, out outBool))
{
GetLoan(claim).FhaInsurance = outBool;
FieldWasSaved = true;
}
break;
case "Loan.UnpaidPrincipalBalance":
if (decimal.TryParse(value, out outDec))
{
GetLoan(claim).UnpaidPrincipalBalance = outDec;
FieldWasSaved = true;
}
break;
case "Loan.Mortgagor_MortgagorID":
if(Int32.TryParse(value, out outInt)){
GetLoan(claim).Mortgagor_MortgagorID = outInt;
FieldWasSaved = true;
}
break;
case "Loan.SystemDefaultDate":
if (DateTime.TryParse(value, out outDT))
{
GetLoan(claim).SystemDefaultDate = outDT;
FieldWasSaved = true;
}
break;
//And so on for 5 billion more cases
}
db.SaveChanges();
return FieldWasSaved;
}
Is there anyway to do this in a generic way or is this super switch actually necessary?
A BIT MORE CONTEXT
I don't claim to understand all the magic the other dev is up to, but basically the string "Loan.FieldName" is coming from some metadata tagged on to an HTML input tag. This is used in this switch to link a particular field to an entity framework data table / property combo. Although this is coming from a strongly typed view, for reasons beyond the ken of man this mapping has become the glue holding the whole thing together.
Usually when I refactor, it's to reduce the complexity of the code somehow, or make it easier to understand. In the code you posted, I have to say it doesn't seem all that complex (although there might be a lot of lines, it looks pretty repetitive and straight-forward). So other than the code aesthetics, I'm not sure how much you are going to gain by refactoring a switch.
Having said that, I might be tempted to create a dictionary where they key is field and the value is a delegate which contains the code for each case (each method would probably return a bool with the FieldWasSaved value, and would have some out-params for those 4 other values). Then your method would just use field to look up the delegate from the dictionary and then call it.
Of course, I would probably just leave the code as-is. The dictionary approach might not be as obvious to other devs, and probably makes the code less obvious to understand.
Update: I also agree with nightwatch that the best refactor will probably involve code which is not shown -- perhaps a lot of this code belongs in other classes (maybe there would be a Loan class which encapsulates all the Loan fields, or something like that...).
If the names in the case statement match with properties in the class, I would change it all to use reflection.
For example, here is a trimmed down version of the core of our base business record, which we use to move data in and out of databases, forms, web services, etc.
public static void SetFieldValue(object oRecord, string sName, object oValue)
{
PropertyInfo theProperty = null;
FieldInfo theField = null;
System.Type oType = null;
try
{
oType = oRecord.GetType();
// See if the column is a property in the record
theProperty = oType.GetProperty(sName, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public, null, null, new Type[0], null);
if (theProperty == null)
{
theField = oType.GetField(sName, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public);
if (theField != null)
{
theField.SetValue(oRecord, Global.ValueFromDB(oValue, theField.FieldType.Name));
}
}
else
{
if (theProperty.CanWrite)
{
theProperty.SetValue(oRecord, Global.ValueFromDB(oValue, theProperty.PropertyType.Name), null);
}
}
}
catch (Exception theException)
{
// Do something useful here
}
}
In the above, Global.ValueFromDB is a big switch statement that safely converts the value to the specified type. Here is a partial version of that:
public static object ValueFromDB(object oValue, string sTypeName)
{
switch (sTypeName.ToLower())
{
case "string":
case "system.string":
return StrFromDB(oValue);
case "boolean":
case "system.boolean":
return BoolFromDB(oValue);
case "int16":
case "system.int16":
return IntFromDB(oValue);
case "int32":
case "system.int32":
return IntFromDB(oValue);
Where the datatype specific FromDBs look something like:
public static string StrFromDB(object theValue)
{
return StrFromDB(theValue, "");
}
public static string StrFromDB(object theValue, string sDefaultValue)
{
if ((theValue != DBNull.Value) && (theValue != null))
{
return theValue.ToString();
}
else
{
return sDefaultValue;
}
}
public static bool BoolFromDB(object theValue)
{
return BoolFromDB(theValue, false);
}
public static bool BoolFromDB(object theValue, bool fDefaultValue)
{
if (!(string.IsNullOrEmpty(StrFromDB(theValue))))
{
return Convert.ToBoolean(theValue);
}
else
{
return fDefaultValue;
}
}
public static int IntFromDB(object theValue)
{
return IntFromDB(theValue, 0);
}
public static int IntFromDB(object theValue, int wDefaultValue)
{
if ((theValue != DBNull.Value) && (theValue != null) && IsNumeric(theValue))
{
return Convert.ToInt32(theValue);
}
else
{
return wDefaultValue;
}
}
It may not seem like your saving much code in the short term, but you will find many, many uses for this once it is implemented (we certainly have).
I know it is gosh to answer your own question, but here is how my boss solved this problem using reflection and a dictionary. I ironically, he finished his solution just minutes after we finished the "Mother of All Switches". No one wants to see an afternoon of typing rendered pointless, but this solution is a lot more slick.
public JsonResult SaveField(int claimId, string field, string value)
{
try
{
var claim = db.Claims.Where(c => c.ClaimID == claimId).SingleOrDefault();
if (claim != null)
{
if(FieldSave(claim, field, value))
return Json(new DataProcessingResult { Success = true, Message = "" });
else
return Json(new DataProcessingResult { Success = false, Message = "Save Failed - Could not parse " + field });
}
else
return Json(new DataProcessingResult { Success = false, Message = "Claim not found" });
}
catch (Exception e)
{
//TODO Make this better
return Json(new DataProcessingResult { Success = false, Message = "Save Failed" });
}
}
bool FieldSave(Claim claim, string field, string value)
{
//our return value
bool FieldWasSaved = true;
string[] path = field.Split('.');
var subObject = GetMethods[path[0]](this, claim);
var secondParams = path[1];
PropertyInfo propertyInfo = subObject.GetType().GetProperty(secondParams);
if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
FieldWasSaved = SetValue[Nullable.GetUnderlyingType(propertyInfo.PropertyType)](propertyInfo, subObject, value);
}
else
{
FieldWasSaved = SetValue[propertyInfo.PropertyType](propertyInfo, subObject, value);
}
db.SaveChanges();
return FieldWasSaved;
}
// these are used for dynamically setting the value of the field passed in to save field
// Add the object look up function here.
static Dictionary<string, Func<dynamic, dynamic, dynamic>> GetMethods = new Dictionary<string, Func<dynamic, dynamic, dynamic>>()
{
{ "Loan", new Func<dynamic, dynamic, dynamic>((x, z)=> x.GetLoan(z)) },
// and so on for the 15 or 20 model classes we have
};
// This converts the string value comming to the correct data type and
// saves the value in the object
public delegate bool ConvertString(PropertyInfo prop, dynamic dynObj, string val);
static Dictionary<Type, ConvertString> SetValue = new Dictionary<Type, ConvertString>()
{
{ typeof(String), delegate(PropertyInfo prop, dynamic dynObj, string val)
{
if(prop.PropertyType == typeof(string))
{
prop.SetValue(dynObj, val, null);
return true;
}
return false;
}
},
{ typeof(Boolean), delegate(PropertyInfo prop, dynamic dynObj, string val)
{
bool outBool = false;
if (Boolean.TryParse(val, out outBool))
{
prop.SetValue(dynObj, outBool, null);
return outBool;
}
return false;
}
},
{ typeof(decimal), delegate(PropertyInfo prop, dynamic dynObj, string val)
{
decimal outVal;
if (decimal.TryParse(val, out outVal))
{
prop.SetValue(dynObj, outVal, null);
return true;
}
return false;
}
},
{ typeof(DateTime), delegate(PropertyInfo prop, dynamic dynObj, string val)
{
DateTime outVal;
if (DateTime.TryParse(val, out outVal))
{
prop.SetValue(dynObj, outVal, null);
return true;
}
return false;
}
},
};
One possibility is to create a Dictionary that has the field name as the key, and a delegate as the value. Something like:
delegate bool FieldSaveDelegate(Claim claim, string value);
You can then write separate methods for each field:
bool SystemDefaultDateHandler(Claim cliaim, string value)
{
// do stuff here
}
And to initialize it:
FieldSaveDispatchTable = new Dictionary<string, FieldSaveDelegate>()
{
{ "Loan.SystemDefaultDate", SystemDefaultDateHandler },
// etc, for five billion more fields
}
The dispatcher, then:
FieldSaveDelegate dlgt;
if (!FieldSaveDispatchTable.TryGetValue(fieldName, out dlgt))
{
// ERROR: no entry for that field
}
dlgt(claim, value);
This would probably be more maintainable than the switch statement, but it's still not especially pretty. The nice thing is that the code to populate the dictionary could be automatically generated.
As a previous answer said, you can use reflection to look up the field name to ensure that it's valid, and check the type (also with reflection). That would reduce the number of handler methods you write to just a handful: one for each individual type.
Even if you didn't use reflection, you could reduce the number of methods required by saving the type, rather than the delegate, in the dictionary. Then you'd have a lookup to get the type for a field, and a small switch statement on the type.
That assumes, of course, that you don't do any special per-field processing. If you have to do special validation or additional processing on some fields, then you'll either need the one method per field, or you'll need some additional per-field information.
Depending on your application, you might be able to redefine Claim to be a dynamic object:
class Claim : DynamicObject
{
... // Current definition
public override bool TrySetMember(SetMemberBinder binder, object value)
{
try
{
var property = typeof(Loan).GetProperty(binder.Name);
object param = null;
// Find some way to parse the string into a value of appropriate type:
// (This will of course need to be improved to handle more types)
if (property.PropertyType == typeof(Int32) )
{
param = Int32.Parse(value.ToString());
}
// Set property in the corresponding Loan object
property.SetValue(GetLoan(this), param, null);
db.Save();
}
catch
{
return base.TrySetMember(binder, value);
}
return true;
}
}
If this is possible, you should be able to use the following syntax to work indirectly with the corresponding Loan objects through a Claim object:
dynamic claim = new Claim();
// Set GetLoan(claim).Mortgagor_MortgagorID to 1723 and call db.Save():
claim.Mortgagor_MortgagorID = "1723";
For the failed cases, when the input string can't be parsed, you will unfortunately get a RuntimeBinderException rather than a nice function return value, so you need to consider if this will be a good fit for you case.
Related
I have a process that allows users to upload data in Excel file and save to database once the data have gone series of validations . Once such validation is data type validation, to prevent them trying to put a string into integer field, for example. Here is an excerpt of the code. The caller (ValidateContentDataType) calls ValidateDataType() and passes property info and data in string to the callee to perform TryParse.
public void ValidateContentDataType()
{
//Do stuff
ValidateDataType(typeof(T).GetProperty("nameOfProperty"), data);
//Do stuff
}
private bool ValidateDataType(PropertyInfo propInfo, string dataElement)
{
if (propInfo is null) return true;
if (propInfo.PropertyType == typeof(decimal) || propInfo.PropertyType == typeof(decimal?))
{
return decimal.TryParse(dataElement, out decimal temp);
}
//Other ifs TryParse for different data type....
return true;
}
While this works, I don't like the series of ifs in ValidateDataType(). Instead of series of ifs for different data types, like this:
if (propInfo.PropertyType == typeof(decimal) || propInfo.PropertyType == typeof(decimal?))
{
return decimal.TryParse(dataElement, out decimal temp);
}
is it possible to have something like this:
return *propertyType*.TryParse(dataElement, out *propertyType *temp);
Stylistically, I'd probably write that as a switch statement:
switch (propInfo.PropertyType)
{
case typeof(decimal):
case typeof(decimal?):
return decimal.TryParse(dataElement, out decimal temp);
case typeof(int):
case typeof(int?):
return ...
}
I think that looks a lot better than multiple if statements. But I freely admit that it's a subjective opinion.
There might be a solution that involves creating a generic method that expects a type that implements the IParseable interface. Something like:
private bool ValidateDataType<T>(...) where T:IParseable
But I haven't completely worked it out. Might be that you'd have to call the method through reflection.
OK. IParsable gives me what I need. Creating a couple of extensions with the the type parameter T that implements IParsable. Then invoke the extensions so I can use the property types from reflection. Seems to be a lot of work for something seemingly trivia. Anyway, here is what I came up with:
internal class Program
{
static void Main(string[] args)
{
Type myClassType = typeof(MyClass);
PropertyInfo propInfo = myClassType.GetProperty("ForecastYear");
ValidateDataType(propInfo.PropertyType, "2023");
}
static void ValidateDataType(Type propertyType, string input)
{
MethodInfo method = typeof(TryParseExtensions).GetMethod(nameof(TryParseExtensions.TryParse));
MethodInfo generic = method.MakeGenericMethod(propertyType);
object[] args = { input, null };
var parseSuccessful = (bool)generic.Invoke(null, args);
var parsedValue = args[1];
}
}
static class TryParseExtensions
{
public static T Parse<T>(this string s) where T : IParsable<T>
{
return T.Parse(s, null);
}
public static bool TryParse<T>(this string? s, [MaybeNullWhen(false)] out T result) where T : IParsable<T>
{
result = default(T);
if (s == null) { return false; }
try
{
result = s.Parse<T>();
return true;
}
catch { return false; }
}
}
public class MyClass{
public int ForecastYear { get; set; }
}
I have an instance of the Account class. Each account object has an owner, reference, etc.
One way I can access an accounts properties is through accessors like
account.Reference;
but I would like to be able to access it using dynamic string selectors like:
account["PropertyName"];
just like in JavaScript. So I would have account["Reference"] which would return the value, but I also would like to be able to assign a new value after that like:
account["Reference"] = "124ds4EE2s";
I've noticed I can use
DataBinder.Eval(account,"Reference")
to get a property based on a string, but using this I can't assign a value to the property.
Any idea on how I could do that?
First of all, you should avoid using this; C# is a strongly-typed language, so take advantage of the type safety and performance advantages that accompany that aspect.
If you have a legitimate reason to get and set the value of a property dynamically (in other words, when the type and/or property name is not able to be defined in the code), then you'll have to use reflection.
The most inline-looking way would be this:
object value = typeof(YourType).GetProperty("PropertyName").GetValue(yourInstance);
...
typeof(YourType).GetProperty("PropertyName").SetValue(yourInstance, "value");
However, you can cache the PropertyInfo object to make it more readable:
System.Reflection.PropertyInfo prop = typeof(YourType).GetProperty("PropertyName");
object value = prop.GetValue(yourInstance);
...
prop.SetValue(yourInstance, "value");
You could try combining the indexer with reflection...
public object this[string propertyName]
{
get
{
PropertyInfo property = GetType().GetProperty(propertyName);
return property.GetValue(this, null);
}
set
{
PropertyInfo property = GetType().GetProperty(propertyName);
property.SetValue(this,value, null);
}
}
If they are your own objects you could provide an indexer to access the fields. I don't really recommend this but it would allow what you want.
public object this[string propertyName]
{
get
{
if(propertyName == "Reference")
return this.Reference;
else
return null;
}
set
{
if(propertyName == "Reference")
this.Reference = value;
else
// do error case here
}
}
Note that you lose type safety when doing this.
I used the reflection method from Richard, but elaborated the set method to handle other types being used such as strings and nulls.
public object this[string propertyName]
{
get
{
PropertyInfo property = GetType().GetProperty(propertyName);
return property.GetValue(this, null);
}
set
{
PropertyInfo property = GetType().GetProperty(propertyName);
Type propType = property.PropertyType;
if (value == null)
{
if (propType.IsValueType && Nullable.GetUnderlyingType(propType) == null)
{
throw new InvalidCastException();
}
else
{
property.SetValue(this, null, null);
}
}
else if (value.GetType() == propType)
{
property.SetValue(this, value, null);
}
else
{
TypeConverter typeConverter = TypeDescriptor.GetConverter(propType);
object propValue = typeConverter.ConvertFromString(value.ToString());
property.SetValue(this, propValue, null);
}
}
}
The SetValue() function will throw an error if the conversion doesn't work.
If you are using .Net 4 you can use the dynamic keyword now.
dynamic foo = account;
foo.Reference = "124ds4EE2s";
I agree with the previous posters that you probably do need to be using the properties. Reflection is very slow compared to direct property access.
On the other hand, if you need to maintain a list of user-defined properties, then you can't use C# properties. You need to pretend you are a Dictionary, or you need to expose a property that behaves like a Dictionary. Here is an example of how you could make the Account class support user-defined properties:
public class Account
{
Dictionary<string, object> properties;
public object this[string propertyName]
{
get
{
if (properties.ContainsKey[propertyName])
return properties[propertyName];
else
return null;
}
set
{
properties[propertyName] = value;
}
}
}
I personally prefer to work with extension methods so here is my code :
public static class ReflectionExtensions
{
public static void SetPropertyValue(this object Target,
string PropertyName,
object NewValue)
{
if (Target == null) return; //or throw exception
System.Reflection.PropertyInfo prop = Target.GetType().GetProperty(PropertyName);
if (prop == null) return; //or throw exception
object value = prop.GetValue(Target, null);
prop.SetValue(Target, NewValue, null);
}
}
You need to use Reflection:
PropertyInfo property = typeof(Account).GetProperty("Reference");
property.SetValue(myAccount, "...", null);
Note that this will be very slow.
Use reflection and expression bodies
public dynamic this[string memberName]
{
get => GetType().GetProperty(memberName).GetValue(this, null);
set => GetType().GetProperty(memberName).SetValue(this,value, null);
}
how to access the list in an object using reflection by string name
public List Table1 { get; set; } = new List();
Here is a simple example, I hope it helps
static void Main(string[] args)
{
Operators op = new Operators()
{
ID = 1,
Name = "Edward",
email = "e.lorenz#mail.com",
Pass = "123456",
Auth1 = "EDF3242523#FFSDGDF"
};
var typei = op.GetType();
var ss = typei.GetProperties().Where(m => m.GetCustomAttributes<Password>().Count() > 0);
foreach (var item in ss)
{
var text = typei.GetProperty(item.Name).GetValue(op).ToString();
typei.GetProperty(item.Name).SetValue(op, Encrypt(text));
}
Console.WriteLine(op.Pass);
Console.WriteLine(op.Auth1);
Console.ReadKey();
}
I'm trying to parse some data out of many old assemblies from a 5-year old open source project and have mostly got it working, but the code is extremely verbose.
Edit: Also, I'm, trapped on .Net Framework 3.5
There are two members I'm interested in are named "Version" and "TargetVersion".
Until recently the "Version" member was a string defined in various ways. It's now replaced with a "ModVersion" member that grabs from assembly version. Some examples:
public string Version => "1.4";
public static readonly string Version = "1.8.14";
public const string Version = "2.8";
public static Version ModVersion => typeof(MainClass).Assembly.GetName().Version;
The TargetVersion member only has two forms: public static readonly uint or public const uint.
So currently I get the member type, and then have nested if .. else if ... for various member types like so:
string dirty = string.Empty;
MemberTypes memberType = GetMemberType(mod, "Version");
if (memberType == MemberTypes.Property) {
Log.Info("It's a property");
dirty = mod
.GetProperty("Version", PUBLIC_STATIC)
.GetValue(mod, null)
.ToString();
} else if (memberType == MemberTypes.Field) {
Log.Info("It's a field");
dirty = mod
.GetField("Version", PUBLIC_STATIC)
.GetValue(mod)
.ToString();
} else if (memberType == MemberTypes.Method) {
Log.Info("It's a method");
dirty = mod
.GetMethod("Version", PUBLIC_STATIC)
.Invoke(null, null)
.ToString();
} else {
Log.Info("Version: Unsupported member type or not found");
}
And then I've got a similar pile of code for getting the "TargetVersion" which is a uint. And now I'll need to add something else to get the new "ModVersion" in cases where "Version" is not found...
Is there any way I can reduce duplication? For example, is it possible to use generics? (I'm still newbie at C#) to avoid duplicating the code for the string vs uint vs. Version? And is there a way to chop down the amount of code that deals with the different member types?
I've seen something like this elsewhere on SO but no idea how to adapt it to my use case:
MemberInfo info = type.GetField(memberName) as MemberInfo ??
type.GetProperty(memberName) as MemberInfo;
What I'm ultimately hoping to achieve is something where I can specify a type and member name and just get the value back. Sort of a bool TryGetMemberValue<T>(Type thing, string memberName, out <T>value) method.
Hope this question isn't too dumb, I'm still learning the basics :o
You were nearly there, maybe something like this
Note : I have just returned a string. You could pass another generic parameter in if you like and cast the results, however i assume all the types you want to work with will override ToString
public static bool TryGetValue<T>(T instance, string name, out string value)
{
value = default;
var member = typeof(T).GetMember(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
.FirstOrDefault();
if (member == null)return false;
switch (member.MemberType)
{
case MemberTypes.Field:
value = (member as FieldInfo)?.GetValue(instance).ToString();
break;
case MemberTypes.Method:
value = (member as MethodInfo)?.Invoke(instance, null).ToString();
break;
case MemberTypes.Property:
value = (member as PropertyInfo)?.GetValue(instance).ToString();
break;
default:
return false;
}
return true;
}
or if you really want the type explicitly
public static bool TryGetValue<T,T2>(T instance, string name, out string value)
{
...
switch (member.MemberType)
{
case MemberTypes.Field:
value = (T2)(member as FieldInfo)?.GetValue(instance);
...
}
Full Demo Here
Update
Here is a version where you give the type, and it spins up an instance to get the instance members, then disposes if it needs to
public static T GetValue<T>(object instance , MemberInfo member)
{
switch (member.MemberType)
{
case MemberTypes.Field: return (T)(member as FieldInfo)?.GetValue(instance);
case MemberTypes.Method: return (T)(member as MethodInfo)?.Invoke(instance, null);
case MemberTypes.Property: return (T)(member as PropertyInfo)?.GetValue(instance);
default:return default;
}
}
public static bool TryGetValue<T>(Assembly asm, string className, string name, out T value)
{
value = default;
var type = asm.GetType(className);
var instance = Activator.CreateInstance(type);
try
{
var member = type.GetMember(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
.FirstOrDefault();
if (instance == null || member == null) return false;
value = GetValue<T>(instance, member);
}
finally
{
(instance as IDisposable)?.Dispose();
}
return true;
}
Usage
Assembly asm = <some assembly>;
if(TryGetValue<uint>(asm, "MyNameSpace.Test1", "TargetVersion", out var out1))
Console.WriteLine(out1);
I'm on learning for C#.
I heared C# is one of the most constructible language. so would you guys make my code more elegant and efficient?
public class ISO639
{
public enum ISO639Code
{
Afrikaans, //af
Albanian, //sq
Amharic, //am
...
Yiddish, //yi
Unknown
}
public static string GetISO639CodeString(ISO639.ISO639Code l)
{
switch (l)
{
case ISO639Code.English: return "en";
case ISO639Code.Japanese: return "ja";
...
case ISO639Code.Hebrew: return "he";
default: return "";
}
public static ISO639.ISO639Code GetISO39CodeValue(string s)
{
switch (s)
{
case "ko" : return ISO639Code.Korean;
case "en" : return ISO639Code.English;
...
case "hu" : return ISO639Code.Hungarian;
default: return ISO639Code.Unknown;
}
}
}
Here is a my class ISO639. This class provides enum for ISO639 code, but I need a type conversion on from ISO639 enum to plain string. (ex. ISO639.ISO639Code.Italian => "it"). I also need a type conversion from plain string to ISO639 enum. (ex. "it" => ISO639.ISO639Code.Italian).
Is there a more efficient coding style for that?
You can add standard System.ComponentModel.Description attribute to each enum entry and then read it.
public enum ISO639Code
{
[Description("af")]
Afrikaans
}
public static class EnumExtensions
{
// Extension method to read Description value
public static string GetDescription(this Enum currentEnum)
{
var fi = currentEnum.GetType().GetField(currentEnum.ToString());
var da = (DescriptionAttribute)Attribute.GetCustomAttribute(fi, typeof(DescriptionAttribute));
return da != null ? da.Description : currentEnum.ToString();
}
}
// **How-to read it**
ISO639Code isoCode = ISO639Code.Afrikaans;
// this will returns "af"
string isoDescription = isoCode.GetDescription();
EDIT:
string searchFor = "af";
ISO639Code foundEntry;
// Loop through all entries descriptions
var allEntries = Enum.GetValues(typeof(ISO639Code));
foreach (var entry in allEntries)
{
// If you will extract this as separate method and use it for search not only loop
// through the all entries - you can put here is yield return description
var currentEntry = ((ISO639Code)entry);
string description = currentEntry.GetDescription();
if (description == searchFor)
{
foundEntry = currentEntry;
break;
}
}
Sure. You can use attributes:
public enum ISO639Code
{
[CodeString("af")] Afrikaans,
[CodeString("sq")] Albanian,
}
Use dictionary, for example: new Dictionary<ISO639Code, string>.
I suggest you to use C# extension methods to enums, they allow you to add whatever logic you want.
For example, see http://pietschsoft.com/post/2008/07/c-enhance-enums-using-extension-methods.aspx
I'd simply store the information in a dictionary-like object. This way you can reference the name by key and get the value directly.
You have an enum:
public enum ISO639Code
{
Afrikaans = 1,
Albanian = 2,
Amharic = 3,
etc.
Create a database table:
ISO639Id int PK,
ISO639Code char(2)
Where the ISO639Id maps to the value of the enum.
In code you'd want a ISO630 Class containing Id and Code values read from the database.
(You can load this once and then cache it in memory.)
The beauty of this approach, is it can be easily extended so that if in future you wanted to store more pieces of information for each ISO639 code, you could simply add another field.
Look at System.Globailzation namespace. The functionality you require looks to be already implemented there. At worst you can see the architecture and technique applied in the .Net framework to solve a very similar problem.
Enumerations are really good to work in code, as they are really strongly typed and make refactoring easier.
Follow these steps:
Use attributes for whatever extra information you want to attach to an enum. Usually this is a simple Description attribute. Something like:
public enum IsoCodes
{
[Description("af")]
Africans = 0,
[Description("am")]
Americans = 1
}
Then write some extension methods to convert strings and integers to and from this enum:
public static string GetDescription(this Enum value)
{
var entries = value.ToString().Split(FlagEnumSeparatorCharacter);
var description = new string[entries.Length];
for (var i = 0; i < entries.Length; i++)
{
var fieldInfo = value.GetType().GetField(entries[i].Trim());
var attributes = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];
description[i] = (attributes.Length > 0) ? attributes[0].Description : entries[i].Trim();
}
return String.Join(", ", description);
}
public static int GetValue(this Enum value)
{
return (int)value.GetType().GetField(value.ToString()).GetRawConstantValue();
}
public static T ToEnum<T>(this string value)
{
if (typeof(T).BaseType.Name != typeof(Enum).Name)
{
throw new Exception("Not an enum");
}
return (T)Enum.Parse(typeof(T), value, true);
}
public static T ToEnum<T>(this int value)
{
if (typeof(T).BaseType.Name != typeof(Enum).Name)
{
throw new Exception("Not an enum");
}
return (T)Enum.ToObject(typeof(T), value);
}
Now use your enums as you like.
I would go with having ISO639Code as class instead of enum:
public class ISO639Code
{
public string Value { get; set ; }
public string Code { get; set; }
public ISO639Code()
{
this.Value = "";
this.Code = "";
}
public ISO639Code(string value, string code)
: this()
{
this.Value = value;
this.Code = code;
}
public override bool Equals(object obj)
{
if (obj != null)
{
if (obj is string)
return obj.ToString().Equals(this.Value, StringComparison.CurrentCultureIgnoreCase);
if (obj is ISO639Code)
return ((ISO639Code)obj).Value.Equals(this.Value, StringComparison.CurrentCultureIgnoreCase);
}
return false;
}
public override int GetHashCode()
{
return this.Value.GetHashCode();
}
public override string ToString()
{
return this.Value;
}
}
Then have global List<ISO639Code> with all possible codes, and to find specific code based on code name or value, just search for this in the List.
Personally, I prefer this over tweaking the enum.
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
What is the best way to compare two entity framework entities?
I want to know the most efficient way of comparing two entities of the same type.
One entity is created from an xml file by hand ( ie new instance and manually set properties) and the other is retvied from my object context.
I want to know if the property values are the same in each instance.
My first thoughts are to generate a hash of the property values from each object and compare the hashes, but there might be another way, or a built in way?
Any suggestions would be welcome.
Many thanks,
James
UPDATE
I came up with this:
static class ObjectComparator<T>
{
static bool CompareProperties(T newObject, T oldObject)
{
if (newObject.GetType().GetProperties().Length != oldObject.GetType().GetProperties().Length)
{
return false;
}
else
{
var oldProperties = oldObject.GetType().GetProperties();
foreach (PropertyInfo newProperty in newObject.GetType().GetProperties())
{
try
{
PropertyInfo oldProperty = oldProperties.Single<PropertyInfo>(pi => pi.Name == newProperty.Name);
if (newProperty.GetValue(newObject, null) != oldProperty.GetValue(oldObject, null))
{
return false;
}
}
catch
{
return false;
}
}
return true;
}
}
}
I haven't tested it yet, it is more of a food for thought to generate some more ideas from the group.
One thing that might be a problem is comparing properties that have entity values themselves, if the default comparator compares on object reference then it will never be true. A possible fix is to overload the equality operator on my entities so that it compares on entity ID.
As is the code will not do what you are expecting.
Try this simple test:
class A {
public int Id { get; set; }
public string Name { get; set; }
}
class B : A {
public DateTime BirthDate { get; set; }
}
class ObjectComparer {
public static void Show() {
A a = new A();
B b = new B();
A a1 = new A();
Console.WriteLine(ObjectComparator.CompareProperties(a, b));
Console.WriteLine(ObjectComparator.CompareProperties(b, a));
Console.WriteLine(ObjectComparator.CompareProperties(a, a1));
}
}
You would expect it to return
false
false
true
but it returns
false
false
false
try changing the inner if to look like:
if (!object.Equals(newProperty.GetValue(newObject, null), oldProperty.GetValue(oldObject, null))) {
return false;
}
You can also save some time in the case a and a1 both reference the same object by checking that in the begining of the method.
static class ObjectComparator {
public static bool CompareProperties(T newObject, T oldObject) {
if (object.Equals(newObject, oldObject)) {
return true;
}
if (newObject.GetType().GetProperties().Length != oldObject.GetType().GetProperties().Length) {
return false;
}
else {
var oldProperties = oldObject.GetType().GetProperties();
foreach (PropertyInfo newProperty in newObject.GetType().GetProperties()) {
try {
PropertyInfo oldProperty = oldProperties.Single(pi => pi.Name == newProperty.Name);
if (!object.Equals(newProperty.GetValue(newObject, null), oldProperty.GetValue(oldObject, null))) {
return false;
}
}
catch {
return false;
}
}
return true;
}
}
}
If you are concered with performance, you can cache the return of Type.GetProperties into a local variable during the lifetime of the method, since Reflection does not do that by itself at least up to version 3.5 SP1. In doing that you will drop GetProperties calls from four to two.
If you are only expecting to compare objects of exactly the same type (or put another way not compare between base and derived instances), you can further reduce the calls of GetProperties to one.
Hope this helps.
I would do something like this
static class ObjectComparator<T>
{
public static bool CompareProperties(T newObject, T oldObject)
{
if (Equals(newObject, oldObject))
{
return true;
}
PropertyInfo[] newProps = newObject.GetType().GetProperties();
PropertyInfo[] oldProps = oldObject.GetType().GetProperties();
if (newProps.Length != oldProps.Length)
{
return false;
}
foreach (PropertyInfo newProperty in newProps)
{
PropertyInfo oldProperty = oldProps.SingleOrDefault(pi => pi.Name == newProperty.Name);
if (oldProperty == null)
return false;
object newval = newProperty.GetValue(newObject, null);
object oldval = oldProperty.GetValue(oldObject, null);
if (!Equals(newval, oldval))
return false;
}
return true;
}
}
You will get a null reference exception (on the next line) if the line that gets oldproperty cannot find a property with the correct name.