Как вы даете C-Auto-свойству значение по умолчанию, используя пользовательский атрибут? - PullRequest
2 голосов
/ 20 июня 2011

Как задать для свойства C # Auto-Property значение по умолчанию, используя пользовательский атрибут?

Это код, который я хочу увидеть:

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

Я знаю, что не существует встроенного метода для инициализации значения по умолчанию с использованием атрибута - могу ли я написать свой собственный класс, который использует мои пользовательские атрибуты для инициализации значения по умолчанию?

Ответы [ 4 ]

6 голосов
/ 20 июня 2011

Если вы хотите сделать это с 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; }
4 голосов
/ 20 июня 2011

Если спекулировать с помощью выражений, вы можете выполнить инициализацию делегатов и кэшировать их.Это сделает код намного быстрее по сравнению с простым отражением.

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);
    }
}
4 голосов
/ 20 июня 2011

Вы можете использовать вспомогательный класс следующим образом:

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();
    }
}
1 голос
/ 20 июня 2011

Вы можете создать метод, подобный этому:

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);
    }
}

Затем вы можете использовать метод фабрики, который вызывает этот метод, или вызывать его непосредственно из конструктора. Обратите внимание, что такое использование отражения, вероятно, не очень хорошая идея, если вы создаете много объектов таким образом, и производительность важна.

...