如何使用自定义属性为C#Auto-Property提供默认值?

如何使用自定义属性为C#Auto-Property提供默认值?

这是我想看到的代码:

class Person { [MyDefault("William")] public string Name { get; set; } } 

我知道没有内置方法来使用属性初始化默认值 – 我可以编写自己的自定义类来使用我的自定义属性来初始化默认值吗?

您可以使用这样的帮助程序类:

 public class DefaultValueHelper { public static void InitializeDefaultValues(T obj) { var properties = (from prop in obj.GetType().GetProperties() let attr = GetDefaultValueAttribute(prop) where attr != null select new { Property = prop, DefaultValue = attr.Value }).ToArray(); foreach (var p in properties) { p.Property.SetValue(obj, p.DefaultValue, null); } } private static DefaultValueAttribute GetDefaultValueAttribute(PropertyInfo prop) { return prop.GetCustomAttributes(typeof(DefaultValueAttribute), true) .Cast() .FirstOrDefault(); } } 

并在类的构造函数中调用InitializeDefaultValues

 class Foo { public Foo() { DefaultValueHelper.InitializeDefaultValues(this); } [DefaultValue("(no name)")] public string Name { get; set; } } 

编辑:更新版本,生成并缓存委托进行初始化。 这是为了避免每次为给定类型调用方法时都使用reflection。

 public static class DefaultValueHelper { private static readonly Dictionary> _initializerCache; static DefaultValueHelper() { _initializerCache = new Dictionary>(); } public static void InitializeDefaultValues(object obj) { if (obj == null) return; var type = obj.GetType(); Action initializer; if (!_initializerCache.TryGetValue(type, out initializer)) { initializer = MakeInitializer(type); _initializerCache[type] = initializer; } initializer(obj); } private static Action MakeInitializer(Type type) { var arg = Expression.Parameter(typeof(object), "arg"); var variable = Expression.Variable(type, "x"); var cast = Expression.Assign(variable, Expression.Convert(arg, type)); var assignments = from prop in type.GetProperties() let attr = GetDefaultValueAttribute(prop) where attr != null select Expression.Assign(Expression.Property(variable, prop), Expression.Constant(attr.Value)); var body = Expression.Block( new ParameterExpression[] { variable }, new Expression[] { cast }.Concat(assignments)); var expr = Expression.Lambda>(body, arg); return expr.Compile(); } private static DefaultValueAttribute GetDefaultValueAttribute(PropertyInfo prop) { return prop.GetCustomAttributes(typeof(DefaultValueAttribute), true) .Cast() .FirstOrDefault(); } } 

如果您想使用PostSharp(如标记所示),请使用延迟加载方面。 你可以看到我在这里建造的那个http://programmersunlimited.wordpress.com/2011/03/23/postsharp-weaving-community-vs-professional-reasons-to-get-a-professional-license/

使用方面,您可以将默认值应用于单个属性,或者在类级别使用单个声明将其应用于多个属性。

延迟加载方面将使用LocationInterceptionAspect基类。

 [Serializable] [LazyLoadingAspect(AttributeExclude=true)] [MulticastAttributeUsage(MulticastTargets.Property)] public class LazyLoadingAspectAttribute : LocationInterceptionAspect { public object DefaultValue {get; set;} public override void OnGetValue(LocationInterceptionArgs args) { args.ProceedGetValue(); if (args.Value != null) { return; } args.Value = DefaultValue; args.ProceedSetValue(); } } 

然后应用这样的方面

 [LazyLoadingAspect(DefaultValue="SomeValue")] public string MyProp { get; set; } 

如果要使用表达式进行推测,您可以初始化委托并缓存它们。 与纯粹的reflection相比,它将使代码更快。

 internal static class Initializer { private class InitCacheEntry { private Action[] _setters; private object[] _values; public InitCacheEntry(IEnumerable> setters, IEnumerable values) { _setters = setters.ToArray(); _values = values.ToArray(); if (_setters.Length != _values.Length) throw new ArgumentException(); } public void Init(object obj) { for (int i = 0; i < _setters.Length; i++) { _setters[i](obj, _values[i]); } } } private static Dictionary _cache = new Dictionary(); private static InitCacheEntry MakeCacheEntry(Type targetType) { var setters = new List>(); var values = new List(); foreach (var propertyInfo in targetType.GetProperties()) { var attr = (DefaultAttribute) propertyInfo.GetCustomAttributes(typeof (DefaultAttribute), true).FirstOrDefault(); if (attr == null) continue; var setter = propertyInfo.GetSetMethod(); if (setter == null) continue; // we have to create expression like (target, value) => ((TObj)target).setter((T)value) // where T is the type of property and obj is instance being initialized var targetParam = Expression.Parameter(typeof (object), "target"); var valueParam = Expression.Parameter(typeof (object), "value"); var expr = Expression.Lambda>( Expression.Call(Expression.Convert(targetParam, targetType), setter, Expression.Convert(valueParam, propertyInfo.PropertyType)), targetParam, valueParam); var set = expr.Compile(); setters.Add(set); values.Add(attr.DefaultValue); } return new InitCacheEntry(setters, values); } public static void Init(object obj) { Type targetType = obj.GetType(); InitCacheEntry init; if (!_cache.TryGetValue(targetType, out init)) { init = MakeCacheEntry(targetType); _cache[targetType] = init; } init.Init(obj); } } 

你可以创建一个像这样的方法:

 public static void FillProperties(T obj) { foreach (var property in typeof(T).GetProperties()) { var attribute = property .GetCustomAttributes(typeof(DefaultValueAttribute), true) .Cast() .SingleOrDefault(); if (attribute != null) property.SetValue(obj, attribute.Value, null); } } 

然后,您可以使用调用此方法的工厂方法,也可以直接从构造函数中调用它。 请注意,如果以这种方式创建大量对象并且性能很重要,则使用reflection可能不是一个好主意。