UnitTest示例我将如何使用该类:
internal enum TestEnum
{
[System.ComponentModel.Description("Description Attribute")]
[System.Xml.Serialization.XmlAttribute("XML Attribute")]
EnumWithDescription,
[System.ComponentModel.Category("Display name attribute")]
[System.ComponentModel.Description("Description Attribute over category attribute")]
EnumWithXMLAttribute,
EnumNoAttributes,
}
protected EnumAttributeDescriptor CreateEnumAttributeDescriptor()
{
EnumAttributeDescriptor descriptor = new EnumAttributeDescriptor();
descriptor.AddAttributePriority<System.ComponentModel.DescriptionAttribute>("Description");
descriptor.AddAttributePriority(new PropertyDecoration("Category", typeof(System.ComponentModel.CategoryAttribute)));
return descriptor;
}
internal class IntAttributeDescriptor : PropertyAttributeDescriptor
{
public IntAttributeDescriptor()
: base()
{
AddAttributePriority<System.ComponentModel.DescriptionAttribute>("Description");
}
}
internal class EnumSwitchableDescriptor : IObjectDescriptor
{
public string GetDescription(object record)
{
TestEnum item = (TestEnum)record;
switch (item)
{
case TestEnum.EnumWithDescription:
return "This is a test attribute";
case TestEnum.EnumWithXMLAttribute:
return "How did you sneak in here";
default:
throw new NotSupportedException(string.Format("Enumeration not supported {0}", item));
}
}
}
internal class TestObject
{
[System.ComponentModel.Description("Testable integer")]
public int TestInteger
{
get;
set;
}
}
[TestMethod]
public void EnumDescriptionHardCodedTest()
{
ObjectDescription descriptions = new ObjectDescription(new EnumSwitchableDescriptor());
string name = descriptions.GetDisplayName(TestEnum.EnumWithDescription);
Assert.AreEqual("This is a test attribute", name);
}
[TestMethod]
public void EnumDescriptionMissingAttributeTest()
{
ObjectDescription descriptions = new ObjectDescription(CreateEnumAttributeDescriptor());
string name = descriptions.GetDisplayName(TestEnum.EnumNoAttributes);
Assert.IsNull(name);
}
[TestMethod]
public void EnumDescriptionDefaultAttributeTest()
{
ObjectDescription descriptions = new ObjectDefaultableDescription((obj) => { return null; });
string name = descriptions.GetDisplayName(TestEnum.EnumWithDescription);
Assert.AreEqual("EnumWithDescription", name);
}
[TestMethod]
public void EnumDescriptionFromSingleAttributeTest()
{
ObjectDescription descriptions = new ObjectDescription(CreateEnumAttributeDescriptor());
string name = descriptions.GetDisplayName(TestEnum.EnumWithDescription);
Assert.AreEqual("Description Attribute", name);
}
[TestMethod]
public void EnumDescriptionFromObjectTest()
{
ObjectDescription descriptions = new ObjectDescription(CreateEnumAttributeDescriptor());
TestEnum testable = TestEnum.EnumWithXMLAttribute;
string name = descriptions.GetDisplayName(testable);
Assert.AreEqual("Description Attribute over category attribute", name);
}
[TestMethod]
public void IntDescriptionFromObjectTest()
{
TestObject testable = new TestObject();
ObjectDescription descriptor = new ObjectDescription(new IntAttributeDescriptor());
string description = descriptor.GetDisplayName(testable);
Assert.AreEqual("Testable integer", description);
}
我负责获取属性的ObjectInspector类:
public class ObjectInspector
{
public string GetProperty(object record, string propertyName)
{
Type typeOf = record.GetType();
object value = typeOf.GetProperty(propertyName).GetValue(record, null);
if (value == null)
return null;
else
return value.ToString();
}
public bool IsOfType(object record, Type typeOf)
{
Type typeOfRecord = record.GetType();
return typeOfRecord.Equals(typeOf) || typeOfRecord.IsSubclassOf(typeOf);
}
public bool IsOfType<T>(object record)
{
Type typeOf = typeof(T);
return IsOfType(record, typeOf);
}
}
>我的描述符类和接口:
public interface IObjectDescriptor
{
string GetDescription(object record);
}
public abstract class AttributeDescriptor : IObjectDescriptor
{
private readonly List<PropertyDecoration> _attributes;
public AttributeDescriptor()
{
_attributes = new List<PropertyDecoration>();
}
public string GetDescription(object record)
{
var priorities = GetAttributes();
if (priorities == null || record == null)
return null;
var attributes = GetAttributes(record);
if (attributes != null)
return GetAttributeDescription(attributes);
else
return null;
}
public virtual void AddAttributePriority(PropertyDecoration attr)
{
_attributes.Add(attr);
}
public virtual void AddAttributePriority<T>(string propertyName)
{
PropertyDecoration lookup = new PropertyDecoration(propertyName, typeof(T));
AddAttributePriority(lookup);
}
public virtual IEnumerable<PropertyDecoration> GetAttributes()
{
return _attributes;
}
protected abstract object[] GetAttributes(object record);
protected virtual string GetAttributeDescription(object[] attributes)
{
ObjectInspector inspector = new ObjectInspector();
object attribute = null;
// looping from highest precedence
foreach (var attr in GetAttributes())
{
// find a matching attribute on our object
attribute = attributes.FirstOrDefault(p => inspector.IsOfType(p, attr.GetPropertyType()));
if (attribute != null)
{
//return attribute;
return attr.GetDescription(attribute);
}
}
return null;
}
}
public class EnumAttributeDescriptor : AttributeDescriptor
{
protected override object[] GetAttributes(object record)
{
var typeOf = record.GetType();
var info = typeOf.GetMember(record.ToString());
return info[0].GetCustomAttributes(false);
}
}
最后是对象描述类,以实际获得描述:
public class ObjectDescription
{
private IObjectDescriptor _objectDescriptor;
public ObjectDescription(IObjectDescriptor attributePriorities)
{
_objectDescriptor = attributePriorities;
}
public string GetDisplayName(object record)
{
return GetDescription(record, _objectDescriptor);
}
protected virtual string GetDescription(object record, IObjectDescriptor descriptor)
{
return descriptor.GetDescription(record);
}
}
public class ObjectDefaultableDescription : ObjectDescription
{
private Func<object, string> _defaultIfEmpty;
public ObjectDefaultableDescription(Func<object, string> defaultIfEmpty)
: base(null)
{
_defaultIfEmpty = defaultIfEmpty;
}
protected override string GetDescription(object record, IObjectDescriptor descriptor)
{
if (record == null)
return _defaultIfEmpty(record);
else
return record.ToString();
}
private string GetDefaultIfEmpty(object record)
{
if (_defaultIfEmpty != null)
return _defaultIfEmpty(record);
else
return null;
}
}
我想保持它的灵活性,以便随着需求的变化,我可以用不同的属性装饰枚举,即结合使用System.Xml.Serialization.XmlEnumAttribute和System.ComponentModel.DescriptionAttribute,以便UI可以使用Description属性来显示一个很好的名称,而XML序列化程序可以使用XmlEnumAttribute等
我希望在我的v上为此提供一个包装器类繁琐的项目,这样我就不必在任何地方都创建委托方法,但是不确定这种方法。
编辑:重新查看后,我认为GetProperty()方法非常通用,实际上并不是必需只属于一个Enum,所以我将其移到了它自己的ObjectInspector类中。
编辑:经过进一步思考,我决定实际上它与Enum并没有直接关系,而是获得描述的功能从一般属性。我对类进行了一些调整,以尝试将其分开。任何评论将不胜感激。
#1 楼
我没有真正看到XmlAttribute在特定枚举常量上的意义。我认为这将作为TypeConverter枚举本身或枚举类型的属性。我可以看到DisplayName和Description的一些用途。不幸的是,枚举不支持DisplayName。 DynamicObject实际上并没有那么有用,但是也许它会给您一些想法。您可以向类中添加一些其他属性,例如“枚举名称” Desc。我在想这可能对WPF绑定有用,但前提是您将辅助类构造为具有当前值。 (如果您具有当前值,那么我看不到通过非动态包装器获得的收益。)ICustomTypeConverter还提供了一些有趣的功能。这是DynamicObject的传递:using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace TestEnumDO
{
sealed class EnumHelper<T> : DynamicObject, ICustomTypeDescriptor where T: struct // ,System.Enum -- you wished
{
public override IEnumerable<string> GetDynamicMemberNames()
{
return Enum.GetNames(typeof(T));
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
T t;
if(!Enum.TryParse<T>(binder.Name, out t))
return base.TryGetMember(binder, out result);
result = t;
return true;
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(typeof(T));
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(typeof(T));
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(typeof(T));
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(typeof(T));
}
public PropertyDescriptor GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(typeof(T));
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(typeof(T), editorBaseType);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(typeof(T), attributes);
}
public EventDescriptorCollection GetEvents()
{
return TypeDescriptor.GetEvents(typeof(T));
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var values = (T[])Enum.GetValues(typeof(T));
var props = new PropertyDescriptor[values.Length];
for (int i = 0; i < values.Length; i++)
{
props[i] = new EnumPropertyDescriptor(values[i], attributes);
}
return new PropertyDescriptorCollection(props, true);
}
public PropertyDescriptorCollection GetProperties()
{
return GetProperties(null);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
private class EnumPropertyDescriptor : PropertyDescriptor
{
private readonly T _t;
public EnumPropertyDescriptor(T value, Attribute[] atts):base(Enum.GetName(typeof(T), value), atts)
{
_t = value;
}
public override AttributeCollection Attributes
{
get
{
var attributeDefs = typeof(T).GetMember(Name).Single().CustomAttributes;
var attributes = new List<Attribute>();
foreach(var ad in attributeDefs){
var constructorArgs = ad.ConstructorArguments.Select(ca => ca.Value).ToArray<object>();
var attribute = (Attribute)ad.Constructor.Invoke(constructorArgs);
foreach (var na in ad.NamedArguments)
{
if (na.MemberInfo is PropertyInfo)
((PropertyInfo)na.MemberInfo).SetValue(attribute, na.TypedValue.Value);
else if (na.MemberInfo is FieldInfo)
((FieldInfo)na.MemberInfo).SetValue(attribute, na.TypedValue.Value);
else throw new NotImplementedException();
}
attributes.Add(attribute);
}
return new AttributeCollection(attributes.ToArray());
}
}
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get { return typeof(T); }
}
public override object GetValue(object component)
{
return _t;
}
public override bool IsReadOnly
{
get { return true; }
}
public override Type PropertyType
{
get { return typeof(T); }
}
public override void ResetValue(object component)
{
}
public override void SetValue(object component, object value)
{
throw new NotSupportedException();
}
public override bool ShouldSerializeValue(object component)
{
return true;
}
}
}
class Program
{
enum TestEnum
{
[Description("Go One")]
Go1,
[Description("Go Two")]
Go2,
Go3
}
static void Main(string[] args)
{
dynamic helper = new EnumHelper<TestEnum>();
Console.WriteLine("Names:");
foreach (var name in helper.GetDynamicMemberNames())
Console.WriteLine(" " + name);
Console.WriteLine();
Console.WriteLine("Display Names:");
var pdc = TypeDescriptor.GetProperties(helper) as PropertyDescriptorCollection;
var desc = pdc.OfType<PropertyDescriptor>().Single(pd => pd.Name == "Go1").Description;
Console.WriteLine("Go1 Value = {0}, Description = {1}", (int)helper.Go1, desc);
if (Debugger.IsAttached)
{
Console.WriteLine();
Console.WriteLine("Press any key...");
Console.ReadKey();
}
}
}
}
评论
\ $ \ begingroup \ $
布兰农干杯。 xmlAttribute用于将枚举用于序列化并且您要为其提供不同的xml值。我们必须同时使用该属性和描述来为用户提供不同的显示值。
\ $ \ endgroup \ $
– dreza
2012年10月30日19:03
\ $ \ begingroup \ $
我仍然认为枚举声明中的TypeConverter比XmlAttribute更合适,但是我不确定您使用的是什么解串器,以及它是否支持TypeConverters。
\ $ \ endgroup \ $
–布兰农
2012年10月31日,下午3:22
\ $ \ begingroup \ $
System.Xml.Serialization.XmlSerializer
\ $ \ endgroup \ $
– dreza
2012年10月31日下午5:19
评论
目前无法访问VS2010,为什么不从weblogs.asp.net/grantbarrington/archive/2009/01/19/…窃取代码,而是将签名重做为公共静态字符串GetDescription@Leonid。嗯,似乎没有提供足够的灵活性。我如何轻松使用category属性,或者Xml AttributeAttribute。这些没有Description属性,因此我无法进行相应的转换?
对...我不在VS2010面前。如果main worker方法可以为您提供所需的一切,而helper方法使它变得更容易怎么办。用法:公共静态GetCategory(enum){return GetCustomAttributevalue
相关:codereview.stackexchange.com/a/5354/3902
@finnw有点,但不完全是。我想用许多属性装饰我的Enum等,而不仅仅是一个。