簡體   English   中英

如何使用自定義屬性為C#Auto-Property指定默認值?

[英]How do you give a C# Auto-Property a default value using a custom attribute?

如何使用自定義屬性為C#Auto-Property提供默認值?

這是我想看到的代碼:

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

我知道沒有內置方法來使用屬性初始化默認值 - 我可以編寫自己的自定義類來使用我的自定義屬性來初始化默認值嗎?

如果您想使用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; }

您可以使用這樣的幫助程序類:

public class DefaultValueHelper
{
    public static void InitializeDefaultValues<T>(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<DefaultValueAttribute>()
                   .FirstOrDefault();
    }
}

並在類的構造函數中調用InitializeDefaultValues

class Foo
{
    public Foo()
    {
        DefaultValueHelper.InitializeDefaultValues(this);
    }

    [DefaultValue("(no name)")]
    public string Name { get; set; }
}

編輯:更新版本,生成並緩存委托進行初始化。 這是為了避免每次為給定類型調用方法時都使用反射。

public static class DefaultValueHelper
{
    private static readonly Dictionary<Type, Action<object>> _initializerCache;

    static DefaultValueHelper()
    {
        _initializerCache = new Dictionary<Type, Action<object>>();
    }

    public static void InitializeDefaultValues(object obj)
    {
        if (obj == null)
            return;

        var type = obj.GetType();
        Action<object> initializer;
        if (!_initializerCache.TryGetValue(type, out initializer))
        {
            initializer = MakeInitializer(type);
            _initializerCache[type] = initializer;
        }
        initializer(obj);
    }

    private static Action<object> 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<Action<object>>(body, arg);
        return expr.Compile();
    }

    private static DefaultValueAttribute GetDefaultValueAttribute(PropertyInfo prop)
    {
        return prop.GetCustomAttributes(typeof(DefaultValueAttribute), true)
                   .Cast<DefaultValueAttribute>()
                   .FirstOrDefault();
    }
}

如果要使用表達式進行推測,您可以初始化委托並緩存它們。 與純粹的反射相比,它將使代碼更快。

internal static class Initializer
{
    private class InitCacheEntry
    {
        private Action<object, object>[] _setters;
        private object[] _values;

        public InitCacheEntry(IEnumerable<Action<object, object>> setters, IEnumerable<object> 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<Type, InitCacheEntry> _cache = new Dictionary<Type, InitCacheEntry>();

    private static InitCacheEntry MakeCacheEntry(Type targetType)
    {
        var setters = new List<Action<object, object>>();
        var values = new List<object>();
        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<Action<object, object>>(
                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>(T obj)
{
    foreach (var property in typeof(T).GetProperties())
    {
        var attribute = property
            .GetCustomAttributes(typeof(DefaultValueAttribute), true)
            .Cast<DefaultValueAttribute>()
            .SingleOrDefault();
        if (attribute != null)
            property.SetValue(obj, attribute.Value, null);
    }
}

然后,您可以使用調用此方法的工廠方法,也可以直接從構造函數中調用它。 請注意,如果以這種方式創建大量對象並且性能很重要,則使用反射可能不是一個好主意。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM