Get name of a property and declaring class - c#

How can I use reflection to get the name and declaring class of a property of a generic type. The purpose is to get an exception if I read a property where nothing has been written so far. One of the problems is that the check must be independent of the declaring class.
value.GetType().DeclaringType is always null.
using System;
namespace TestCodeContract
{
public struct CheckForNull<T> where T : struct
{
private T? backingField;
public static implicit operator T(CheckForNull<T> value)
{
if (!(value.backingField.HasValue))
{
var t1 = value.GetType().DeclaringType; // always null.
var propertyName = "Delta"; // TODO get from Reflection
var className = "ContractClass"; // TODO get from Reflection
var msg = String.Format("Proprety '{0}' in class '{1}' is not initialized", propertyName, className);
throw new ApplicationException(msg);
}
return value.backingField.Value;
}
public static implicit operator CheckForNull<T>(T value)
{
return new CheckForNull<T> { backingField = value };
}
}
public class ContractClass
{
public CheckForNull<int> Delta { get; set; }
public void Test1()
{
int x = Delta; // Wanted: "Property 'Delta' in class 'ContractClass' is not initialized"
}
}
}

No, you can't do it like that. I would suggest something like this instead:
// You could do this without the constraint, with a bit of extra work.
public class ReadOnlyAfterWrite<T> where T : struct
{
private T? value;
private readonly string property;
private readonly string type;
public ReadOnlyAfterWrite(string property, string type)
{
this.property = property;
this.type = type;
}
public T Value
{
get
{
if (value == null)
{
// Use type and property here
throw new InvalidOperationException(...);
}
return (T) value;
}
set { this.value = value; }
}
}
public class ContractClass
{
// This is what I'd do in C# 6. Before that, probably just use string literals.
private readonly ReadOnlyAfterWrite<int> delta =
new ReadOnlyAfterWrite(nameof(Delta), nameof(ContractClass));
public int Delta
{
get { return delta.Value; }
set { delta.Value = value; }
}
}
While it's not terribly clean in implementation, I think it's a better public API - the fact that it's guarded is invisible to the caller, who just sees an int property.

Related

How to generate an object that has all the Properties of another, only Nullable (when applicable)?

I am creating a project that will manage app configurations. It will be very generic, reusable across different apps (with different config models on each) and very flexible - including the ability to create/save/store/read/merge partial configurations from multiple sources.
Without getting too much into details, here's an example of what I need to do.
I have a class like below:
public class TestConfigModel
{
public int SomeIntValue { get; set; }
public string SomeStringValue { get; set; }
public TestConfigSubsection Subsection { get; set; }
}
public class TestConfigSubsection
{
public System.DayOfWeek SomeSubsectionEnumValue { get; set; }
public Guid SomeSubsectionGuidValue { get; set; }
}
I need to dynamically generate a version of this model that has all properties nullable (unless they already take a null):
public class TestConfigModelNullable
{
public int? SomeIntValue { get; set; }
public string SomeStringValue { get; set; } // already takes a null
public TestConfigSubsection Subsection { get; set; } // already takes a null
}
public class TestConfigSubsectionNullable
{
public System.DayOfWeek? SomeSubsectionEnumValue { get; set; }
public Guid? SomeSubsectionGuidValue { get; set; }
}
Example use:
I have a default (complete) config like so:
var aConfigInstance = new TestConfigModel()
{
SomeIntValue = 3,
SomeStringValue = "hey",
Subsection = new TestConfigSubsection()
{
SomeSubsectionEnumValue = DayOfWeek.Thursday,
SomeSubsectionGuidValue = Guid.Parse("{2C475019-5AAC-43C6-AC87-21947A40E3B7}")
}
};
Now, I need to be able to create, serialize, store and later deserialize and operate on a partial configuration model, like below:
var aPartialConfigInstance = new TestConfigModelNullable()
{
SomeIntValue = 4,
Subsection = new TestConfigSubsection()
{
SomeSubsectionEnumValue = DayOfWeek.Monday
}
};
... with all missing properties null. If I try to do the same with the original class, all the other non-nullable fields will receive default values and that would be bad (how do I tell if int value of 0 is intended or not? Maybe it makes sense for the consumer app).
However, I'm new to reflection in general and not sure how to approach this. Your help would be much appreciated :)
Recall that we don't know the model ahead of time.
I happened to implement several similar mechanisms in several different flavors. Implementing an "automagical" mechanism implies quite a bit of heavy lifting.
Here I wouldn't suggest generating separate nullable versions of the models. Instead, I would opt for making all model properties Optional<T>, which is like Nullable<T> but works for reference types as well. In this way, partial models will be represented with the same types as "base" models.
Such an approach will save the complexity of code generation (T4, Roslyn, CodeDom, or Reflection.Emit -- all these imply a lot of effort, including plugging them into the build process).
In addition, in either approach, a "merging" logic must be implemented which applies a partial model over a "base" one. In code generation approach, the merge logic can be generated as part of the nullable models. In Optional<T> approach, it can be either hard-coded or implemented in generic way with runtime Reflection (not Reflection.Emit). The hard-coded way appears to be the easiest, but for large number of models and properties, runtime Reflection may be a better fit.
How it will look
The models would look like this:
public class TestConfigModel
{
public Optional<int> SomeIntValue { get; set; }
public Optional<string> SomeStringValue { get; set; }
public Optional<TestConfigSubsection> Subsection { get; set; }
}
With the implicit conversion operators of Optional<T>, you'll be able to initialize section values as normally:
var config = new TestConfigModel {
SomeIntValue = 123,
SomeStringValue = "ABC",
Subsection = new TestConfigSubsection {
SomeSubsectionEnumValue = DayOfWeek.Thursday
}
};
Generic merging logic can be implemented by introducing an Apply method to Optional<T>:
Optional<T> Apply(Optional<T> partial, Func<T, T, Optional<T>> merge = null)
Every model will have to implement its own ApplyXxxx() method that will be passed in the merge parameter, like this:
public class TestConfigModel
{
// ...properties
public Optional<TestConfigModel> ApplyModel(TestConfigModel partial)
{
SomeIntValue = SomeIntValue.Apply(partial.SomeIntValue);
SomeStringValue = SomeStringValue.Apply(partial.SomeStringValue);
Subsection = Subsection.Apply(
partial.Subsection,
merge: (left, right) => left.ApplySubsection(right));
return this;
}
}
public class TestConfigSubsection
{
// ...properties
public Optional<TestConfigSubsection> ApplySubsection(TestConfigSubsection partial)
{
SomeSubsectionEnumValue = SomeSubsectionEnumValue.Apply(partial.SomeSubsectionEnumValue);
SomeSubsectionGuidValue = SomeSubsectionGuidValue.Apply(partial.SomeSubsectionGuidValue);
return this;
}
}
Optional<T>
Built-in implementation of Optional<T> is planned for C# 8, but it can be implemented easily (mostly similar to Nullable<T>).
public interface IOptional
{
bool HasValue { get; }
object Value { get; }
}
public struct Optional<T> : IOptional
{
private readonly bool _hasValue;
private readonly T _value;
public Optional(T value)
{
_value = value;
_hasValue = true;
}
public bool HasValue => _hasValue;
object IOptional.Value => Value;
public T Value
{
get
{
if (!_hasValue)
{
throw new InvalidOperationException("has no value");
}
return _value;
}
}
public T GetValueOrDefault() => _value;
public T GetValueOrDefault(T defaultValue)
{
if (!_hasValue)
{
return defaultValue;
}
return _value;
}
public bool IsNullValue => _hasValue && ReferenceEquals(_value, null);
public override bool Equals(object other)
{
if (other is Optional<T> otherOptional)
{
if (_hasValue != otherOptional.HasValue)
{
return false;
}
if (_hasValue)
{
return CompareValue(otherOptional.Value);
}
return true;
}
return false;
}
bool CompareValue(object otherValue)
{
if (_value == null)
{
return (otherValue == null);
}
return _value.Equals(otherValue);
}
public override int GetHashCode()
{
if (_hasValue || ReferenceEquals(_value, null))
{
return 0;
}
return _value.GetHashCode();
}
public override string ToString()
{
if (!_hasValue || ReferenceEquals(_value, null))
{
return "";
}
return _value.ToString();
}
public Optional<T> Apply(Optional<T> partial, Func<T, T, Optional<T>> merge = null)
{
if (!_hasValue && partial.HasValue)
{
return partial;
}
if (_hasValue && partial.HasValue)
{
if (ReferenceEquals(_value, null))
{
return partial.Value;
}
if (!ReferenceEquals(partial.Value, null))
{
if (merge != null)
{
return merge(_value, partial.Value);
}
throw new InvalidOperationException("both values exist and merge not provided");
}
}
return this;
}
public static implicit operator Optional<T>(T value)
{
return new Optional<T>(value);
}
public static explicit operator T(Optional<T> value)
{
return value.Value;
}
}
Serialization
The last thing left is to teach the serializers to handle Optional<T>. For instance, Newtonsoft.Json would require a custom JsonConverter. Below isn't a complete implementation, but it demonstrates the approach:
public class OptionalConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Optional<>);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// TODO: implement properly
// roughly the approach is like this:
var hasValue = reader.ReadAsBoolean().GetValueOrDefault();
var innerValue = hasValue
? serializer.Deserialize(reader, objectType.GetGenericArguments([0])
: null;
return Activator.CreateInstance(
objectType,
innerValue != null ? new[] {innerValue} : new object[0]);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is IOptional optional)
{
// TODO: implement writing
}
}
}
// Just for convenience
public Type CreateNullableTypeFrom<T>()
{
return CreateNullableTypeFrom(typeof(T));
}
public Type CreateNullableTypeFrom(Type typeToConvert)
{
// Get the AssemblyName where the type is defined
AssemblyName assembly = typeToConvert.Assembly.GetName();
AssemblyBuilder dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.Run);
ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule(assembly.Name);
TypeBuilder typeBuilder = dynamicModule.DefineType(typeToConvert.Name + "Nullable");
// Loop through the properties
foreach(PropertyInfo property in typeToConvert.GetProperties())
{
// If property is value type, it can't be null
if(property.PropertyType.IsValueType)
{
// Create a nullable type for the property
typeBuilder.DefineProperty(property.Name, property.Attributes, typeof(Nullable<>).MakeGenericType(property.PropertyType), Type.EmptyTypes);
}
// The property can be null
else
{
// Create a similar property
typeBuilder.DefineProperty(property.Name, property.Attributes, property.PropertyType, Type.EmptyTypes);
}
}
// Finally, create the type
Type convertedType = typeBuilder.CreateType();
Console.WriteLine(convertedType.Name);
// Note: to access the properties of the converted type through reflection,
// use GetRuntimeProperties method, not GetProperties, since GetProperties
// will return an empty array because the type was created an runtime
return convertedType;
}

Is it possible to override the return of an Object in C#?

I have a LiteralObject class and by default it inherits from System.Object. This is how the class looks like:
public LiteralObject(Object value)
{
this.value = value;
}
private Object value;
public Object getValue() { return value; }
To get the value of a LiteralObject object:
LiteralObject x = new LiteralObject(23);
Console.WriteLine(x.getvalue());
What I want is this:
LiteralObject x = new LiteralObject(23);
Console.WriteLine(x);
Is there a way to override the returning of a System.Object object? or is there any other way to get what I want? Bytheway, overriding ToString() is not the solution I'm looking for. And the object passed into the constructor is of the same type with its parent/base class
You can sort of do that using implicit cast operators. However it doesn't work on object since you can't perform implicit conversion on object (or any other ancestor class). To get around that we can use generics:
class LiteralObject<T>
{
public LiteralObject(T initialValue)
{
this.value = initialValue;
}
private T value;
public T getValue()
{
return value;
}
static public implicit operator T(LiteralObject<T> source)
{
return source.getValue();
}
}
public class Program
{
public static void PrintInteger(int i)
{
Console.WriteLine("{0}", i);
}
public static void Main()
{
var o = new LiteralObject<int>(23);
PrintInteger(o);
}
}
Output:
23
Example on DotNetFiddle

Constrain generic to be a nullable type

I'm looking for sample usage something like this:
Foo<string> stringFoo = new Foo<string>("The answer is");
Foo<int> intFoo = new Foo<int>(42);
// The Value of intFoo & stringFoo are strongly typed
stringFoo.Nullify();
intFoo.Nullify();
if (stringFoo == null && intFoo == null)
MessageBox.Show("Both are null);
Given this class Foo, I can auto-wrap T into a nullable:
public class Foo1<T>
where T : struct
{
private T? _value;
public Foo(T? initValue)
{
_value = initValue;
}
public T? Value { get { return _value; } }
public void Nullify { _value = null; }
}
This works for primitives, but not with String or other classes.
Next flavor works for strings, but not primitives:
public class Foo2<T>
{
private T _value;
public Foo(T initValue)
{
_value = initValue;
}
public T Value { get { return _value; } }
public void Nullify { _value = default(T); }
}
I could use Nullable<int> for Foo2 and the code would work like this:
Foo2<int?> intFoo = new Foo<int?>(42);
But this is error prone because it fails for Foo2. If I could constrain T to be types that support nullability then that would be fine.
So after all of that, is there any way to constrain T to be a nullable type?
Some additional notes: .NET 4.0, VS2010. And I did find one similar question to this on here, but without a succesful answer.
There's no constraint you can apply for this, but you can test for it at execution time:
if (default(T) != null)
{
throw new SomeAppropriateException(typeof(T) + " is not a nullable type");
}
You could even put that into a static constructor, which would make sure it only executed once per constructed type - and anyone trying to use Foo<int> anywhere would have a hard time ignoring the TypeInitializerException. That's not terribly friendly for a public API, but I think it's reasonable for an internal one.
EDIT: There is one horrible way of making it harder to create instances of Foo<int>... you could use the ghastly code in this blog post (using overload resolution rules along with default parameters and a couple of constrained generic types) and mark the overload which aims at a non-nullable value type as obsolete with an error. That way, Foo<int> would still be a valid type, but you'd be hard-pressed to create an instance of it. I'm not going to recommend that you do this though...
You might be able to make the constructor of Foo<T> internal, and require that new instances can only be created through a factory class:
public class Foo<T>
{
private T value;
internal Foo(T value)
{
this.value = value;
}
public void Nullify()
{
this.value = default(T);
}
public T Value { get { return this.value; } }
}
public class Foo
{
public static Foo<T> Create<T>(T value) where T : class
{
return new Foo<T>(value);
}
public static Foo<T?> Create<T>(T? value) where T : struct
{
return new Foo<T?>(value);
}
}
I don't like it as much as the syntax of Foo1, but here is Foo3:
public class Foo3<T>
where T : struct
{
private T _value;
private T _nullValue;
public Foo3(T initValue)
: this(initValue, default(T))
{
}
public Foo3(T initValue, T nullValue)
{
_value = initValue;
_nullValue = nullValue;
}
public T Value { get { return _value; } }
public bool IsNull
{
get
{
return object.Equals(_value, _nullValue);
}
}
public void Nullify() { _value = _nullValue; }
}
And then my usage becomes:
Foo3<string> stringFoo = new Foo<string>("The answer is");
Foo3<int> intFoo = new Foo<int>(42, int.MinValue);
stringFoo.Nullify();
intFoo.Nullify();
if (stringFoo.IsNull && intFoo.IsNull)
MessageBox.Show("Both are null);
This is still error prone because getting the Value property of Foo3 (and Foo2) is not straightforward. Foo1 was the best because automatically wrapped the Value will null support.
I might just need ValueTypeFoo and ObjectFoo and deal with two versons.

Reading attribute of a property using reflection

I need to read the attribute of a property using reflection
For example I get the following :
[XmlElement("Id")]
[CategoryAttribute("Main"), ReadOnly(true),
Description("This property is auto-generated")]
[RulesCriteria("ID")]
public override string Id
{
get { return _ID; }
set
{
_ID = value;
}
}
i want to get the " read only "value of this property using reflection
can anybody help
It's difficult to write the code for your case without knowing the Type name. Hope below example helps.
using System;
using System.Reflection;
public class Myproperty
{
private string caption = "Default caption";
public string Caption
{
get{return caption;}
set {if(caption!=value) {caption = value;}
}
}
}
class Mypropertyinfo
{
public static int Main(string[] args)
{
Console.WriteLine("\nReflection.PropertyInfo");
// Define a property.
Myproperty Myproperty = new Myproperty();
Console.Write("\nMyproperty.Caption = " + Myproperty.Caption);
// Get the type and PropertyInfo.
Type MyType = Type.GetType("Myproperty");
PropertyInfo Mypropertyinfo = MyType.GetProperty("Caption");
// Get and display the attributes property.
PropertyAttributes Myattributes = Mypropertyinfo.Attributes;
Console.Write("\nPropertyAttributes - " + Myattributes.ToString());
return 0;
}
}
MSDN
public static bool PropertyReadOnlyAttributeValue(PropertyInfo property)
{
ReadonlyAttribute attrib = Attribute.GetCustomAttribute(property, typeof(ReadOnlyAttribute));
return attrib != null && attrib.IsReadOnly;
}
public static bool PropertyReadOnlyAttributeValue(Type type, string propertyName)
{
return PropertyReadOnlyAttributeValue(type.GetProperty(propertyName));
}
public static bool PropertyReadOnlyAttributeValue(object instance, string propertyName)
{
if (instance != null)
{
Type type = instance.GetType();
return PropertyReadOnlyAttributeValue(type, propertyName);
}
return false;
}

Extending Enums, Overkill?

I have an object that needs to be serialized to an EDI format. For this example we'll say it's a car. A car might not be the best example b/c options change over time, but for the real object the Enums will never change.
I have many Enums like the following with custom attributes applied.
public enum RoofStyle
{
[DisplayText("Glass Top")]
[StringValue("GTR")]
Glass,
[DisplayText("Convertible Soft Top")]
[StringValue("CST")]
ConvertibleSoft,
[DisplayText("Hard Top")]
[StringValue("HT ")]
HardTop,
[DisplayText("Targa Top")]
[StringValue("TT ")]
Targa,
}
The Attributes are accessed via Extension methods:
public static string GetStringValue(this Enum value)
{
// Get the type
Type type = value.GetType();
// Get fieldinfo for this type
FieldInfo fieldInfo = type.GetField(value.ToString());
// Get the stringvalue attributes
StringValueAttribute[] attribs = fieldInfo.GetCustomAttributes(
typeof(StringValueAttribute), false) as StringValueAttribute[];
// Return the first if there was a match.
return attribs.Length > 0 ? attribs[0].StringValue : null;
}
public static string GetDisplayText(this Enum value)
{
// Get the type
Type type = value.GetType();
// Get fieldinfo for this type
FieldInfo fieldInfo = type.GetField(value.ToString());
// Get the DisplayText attributes
DisplayTextAttribute[] attribs = fieldInfo.GetCustomAttributes(
typeof(DisplayTextAttribute), false) as DisplayTextAttribute[];
// Return the first if there was a match.
return attribs.Length > 0 ? attribs[0].DisplayText : value.ToString();
}
There is a custom EDI serializer that serializes based on the StringValue attributes like so:
StringBuilder sb = new StringBuilder();
sb.Append(car.RoofStyle.GetStringValue());
sb.Append(car.TireSize.GetStringValue());
sb.Append(car.Model.GetStringValue());
...
There is another method that can get Enum Value from StringValue for Deserialization:
car.RoofStyle = Enums.GetCode<RoofStyle>(EDIString.Substring(4, 3))
Defined as:
public static class Enums
{
public static T GetCode<T>(string value)
{
foreach (object o in System.Enum.GetValues(typeof(T)))
{
if (((Enum)o).GetStringValue() == value.ToUpper())
return (T)o;
}
throw new ArgumentException("No code exists for type " + typeof(T).ToString() + " corresponding to value of " + value);
}
}
And Finally, for the UI, the GetDisplayText() is used to show the user friendly text.
What do you think? Overkill? Is there a better way? or Goldie Locks (just right)?
Just want to get feedback before I intergrate it into my personal framework permanently. Thanks.
The part you're using to serialize is fine. The deserialization part is awkwardly written. The main problem is that you're using ToUpper() to compare strings, which is easily broken (think globalization). Such comparisons should be done with string.Compare instead, or the string.Equals overload that takes a StringComparison.
The other thing is that performing these lookups again and again during deserialization is going to pretty slow. If you're serializing a lot of data, this could actually be quite noticeable. In that case, you'd want to build a map from the StringValue to the enum itself - throw it into a static Dictionary<string, RoofStyle> and use it as a lookup for the round-trip. In other words:
public static class Enums
{
private static Dictionary<string, RoofStyle> roofStyles =
new Dictionary<string, RoofStyle>()
{
{ "GTR", RoofStyle.Glass },
{ "CST", RoofStyle.ConvertibleSoft },
{ "HT ", RoofStyle.HardTop },
{ "TT ", RoofStyle.TargaTop }
}
public static RoofStyle GetRoofStyle(string code)
{
RoofStyle result;
if (roofStyles.TryGetValue(code, out result))
return result;
throw new ArgumentException(...);
}
}
It's not as "generic" but it's way more efficient. If you don't like the duplication of string values then extract the codes as constants in a separate class.
If you really need it to be totally generic and work for any enum, you can always lazy-load the dictionary of values (generate it using the extension methods you've written) the first time a conversion is done. It's very simple to do that:
static Dictionary<string, T> CreateEnumLookup<T>()
{
return Enum.GetValues(typeof(T)).ToDictionary(o => ((Enum)o).GetStringValue(),
o => (T)o);
}
P.S. Minor detail but you might want to consider using Attribute.GetCustomAttribute instead of MemberInfo.GetCustomAttributes if you only expect there to be one attribute. There's no reason for all the array fiddling when you only need one item.
Personally, I think you are abusing the language and trying to use enums in a way they were never intended. I would create a static class RoofStyle, and create a simple struct RoofType, and use an instance for each of your enum values.
Why don't you create a type with static members such as mikerobi said
Example...
public class RoofStyle
{
private RoofStyle() { }
public string Display { get; private set; }
public string Value { get; private set; }
public readonly static RoofStyle Glass = new RoofStyle
{
Display = "Glass Top", Value = "GTR",
};
public readonly static RoofStyle ConvertibleSoft = new RoofStyle
{
Display = "Convertible Soft Top", Value = "CST",
};
public readonly static RoofStyle HardTop = new RoofStyle
{
Display = "Hard Top", Value = "HT ",
};
public readonly static RoofStyle Targa = new RoofStyle
{
Display = "Targa Top", Value = "TT ",
};
}
BTW...
When compiled into IL an Enum is very similar to this class structure.
... Enum backing fields ...
.field public specialname rtspecialname int32 value__
.field public static literal valuetype A.ERoofStyle Glass = int32(0x00)
.field public static literal valuetype A.ERoofStyle ConvertibleSoft = int32(0x01)
.field public static literal valuetype A.ERoofStyle HardTop = int32(0x02)
.field public static literal valuetype A.ERoofStyle Targa = int32(0x03)
... Class backing fields ...
.field public static initonly class A.RoofStyle Glass
.field public static initonly class A.RoofStyle ConvertibleSoft
.field public static initonly class A.RoofStyle HardTop
.field public static initonly class A.RoofStyle Targa
Here is a base class I use for enumeration classes:
public abstract class Enumeration<T, TId> : IEquatable<T> where T : Enumeration<T, TId>
{
public static bool operator ==(Enumeration<T, TId> x, T y)
{
return Object.ReferenceEquals(x, y) || (!Object.ReferenceEquals(x, null) && x.Equals(y));
}
public static bool operator !=(Enumeration<T, TId> first, T second)
{
return !(first == second);
}
public static T FromId(TId id)
{
return AllValues.Where(value => value.Id.Equals(id)).FirstOrDefault();
}
public static readonly ReadOnlyCollection<T> AllValues = FindValues();
private static ReadOnlyCollection<T> FindValues()
{
var values =
(from staticField in typeof(T).GetFields(BindingFlags.Static | BindingFlags.Public)
where staticField.FieldType == typeof(T)
select (T) staticField.GetValue(null))
.ToList()
.AsReadOnly();
var duplicateIds =
(from value in values
group value by value.Id into valuesById
where valuesById.Skip(1).Any()
select valuesById.Key)
.Take(1)
.ToList();
if(duplicateIds.Count > 0)
{
throw new DuplicateEnumerationIdException("Duplicate ID: " + duplicateIds.Single());
}
return values;
}
protected Enumeration(TId id, string name)
{
Contract.Requires(((object) id) != null);
Contract.Requires(!String.IsNullOrEmpty(name));
this.Id = id;
this.Name = name;
}
protected Enumeration()
{}
public override bool Equals(object obj)
{
return Equals(obj as T);
}
public override int GetHashCode()
{
return this.Id.GetHashCode();
}
public override string ToString()
{
return this.Name;
}
#region IEquatable
public virtual bool Equals(T other)
{
return other != null && this.IdComparer.Equals(this.Id, other.Id);
}
#endregion
public virtual TId Id { get; private set; }
public virtual string Name { get; private set; }
protected virtual IEqualityComparer<TId> IdComparer
{
get { return EqualityComparer<TId>.Default; }
}
}
An implementation would look like:
public sealed class RoofStyle : Enumeration<RoofStyle, int>
{
public static readonly RoofStyle Glass = new RoofStyle(0, "Glass Top", "GTR");
public static readonly RoofStyle ConvertibleSoft = new RoofStyle(1, "Convertible Soft Top", "CST");
public static readonly RoofStyle HardTop = new RoofStyle(2, "Hard Top", "HT ");
public static readonly RoofStyle Targa = new RoofStyle(3, "Targa Top", "TT ");
public static RoofStyle FromStringValue(string stringValue)
{
return AllValues.FirstOrDefault(value => value.StringValue == stringValue);
}
private RoofStyle(int id, string name, string stringValue) : base(id, name)
{
StringValue = stringValue;
}
public string StringValue { get; private set; }
}
You would use it during serialization like this:
var builder = new StringBuilder();
builder.Append(car.RoofStyle.StringValue);
...
To deserialize:
car.RoofStyle = RoofStyle.FromStringValue(EDIString.Substring(4, 3));
I don't see a problem with it - actually, I do the same. By this, I achieve verbosity with the enum, and can define how the enum is to be translated when I use it to request data, eg. RequestTarget.Character will result in "char".
Can't say I've ever seen it done that way but the consumer code is relatively simple so I'd probably enjoy using it.
The only thing that sticks out for me is the potential for consumers having to deal with nulls - which might be able to be removed. If you have control over the attributes (which you do, from the sounds of it), then there should never be a case where GetDisplayText or GetStringValue return null so you can remove
return attribs.Length > 0 ? attribs[0].StringValue : null;
and replace it with
return attribs[0].StringValue;
in order to simplify the interface for consumer code.
IMHO, the design is solid, and will work.
However, reflection tends to be a litle slow, so if those methods are used in tight loops, it might slow down the whole application.
You could try caching the the return values into a Dictionary<RoofStyle, string> so they are only reflected once, and then fetched from cache.
Something like this:
private static Dictionary<Enum, string> stringValues
= new Dictionary<Enum,string>();
public static string GetStringValue(this Enum value)
{
if (!stringValues.ContainsKey(value))
{
Type type = value.GetType();
FieldInfo fieldInfo = type.GetField(value.ToString());
StringValueAttribute[] attribs = fieldInfo.GetCustomAttributes(
typeof(StringValueAttribute), false) as StringValueAttribute[];
stringValues.Add(value, attribs.Length > 0 ? attribs[0].StringValue : null);
}
return stringValues[value];
}
I know this question has already been answered, but while ago I posted the following code fragment on my personal blog, which demonstrates faking Java style enums using extension methods. You might find this method works for you, especially as it overcomes the overhead of accessing Attributes via reflection.
using System;
using System.Collections.Generic;
namespace ScratchPad
{
internal class Program
{
private static void Main(string[] args)
{
var p = new Program();
p.Run();
}
private void Run()
{
double earthWeight = 175;
double mass = earthWeight / Planet.Earth.SurfaceGravity();
foreach (Planet planet in Enum.GetValues(typeof(Planet))) {
Console.WriteLine("Your weight on {0} is {1}", planet, planet.SurfaceWeight(mass));
}
}
}
public enum Planet
{
Mercury,
Venus,
Earth,
Mars,
Jupiter,
Saturn,
Uranus,
Neptune
}
public static class PlanetExtensions
{
private static readonly Dictionary<Planet, PlanetData> planetMap = new Dictionary<Planet, PlanetData>
{
{Planet.Mercury, new PlanetData(3.303e+23, 2.4397e6)},
{Planet.Venus, new PlanetData(4.869e+24, 6.0518e6)},
{Planet.Earth, new PlanetData(5.976e+24, 6.37814e6)},
{Planet.Mars, new PlanetData(6.421e+23, 3.3972e6)},
{Planet.Jupiter, new PlanetData(1.9e+27, 7.1492e7)},
{Planet.Saturn, new PlanetData(5.688e+26, 6.0268e7)},
{Planet.Uranus, new PlanetData(8.686e+25, 2.5559e7)},
{Planet.Neptune, new PlanetData(1.024e+26, 2.4746e7)}
};
private const double G = 6.67300E-11;
public static double Mass(this Planet planet)
{
return GetPlanetData(planet).Mass;
}
public static double Radius(this Planet planet)
{
return GetPlanetData(planet).Radius;
}
public static double SurfaceGravity(this Planet planet)
{
PlanetData planetData = GetPlanetData(planet);
return G * planetData.Mass / (planetData.Radius * planetData.Radius);
}
public static double SurfaceWeight(this Planet planet, double mass)
{
return mass * SurfaceGravity(planet);
}
private static PlanetData GetPlanetData(Planet planet)
{
if (!planetMap.ContainsKey(planet))
throw new ArgumentOutOfRangeException("planet", "Unknown Planet");
return planetMap[planet];
}
#region Nested type: PlanetData
public class PlanetData
{
public PlanetData(double mass, double radius)
{
Mass = mass;
Radius = radius;
}
public double Mass { get; private set; }
public double Radius { get; private set; }
}
#endregion
}
}

Categories