DefaultValue Attribute Based Approach to Property Initializaton

参考资料1:http://www.barsoom.org/defaultvalue-attribute-initial

public class TestClass
{
    [DefaultValue(1)]
    public int IntProperty { get; set; }

    [DefaultValue(1)]
    public long LongProperty { get; set; }

    [DefaultValue(true)]
    public bool BoolProptrty { get; set; }
}
public Constructor()
{
    // Iterate through each property
    foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(this))
    {
        // Set default value if DefaultValueAttribute is present
        DefaultValueAttribute attr = prop.Attributes[typeof(DefaultValueAttribute)]
                                                         as DefaultValueAttribute; 
        if (attr != null)
            prop.SetValue(this, attr.Value);
    }

    ...
}

代码演示:

class Program
    {


        static void Main(string[] args)
        {
            TestClass tc = new TestClass();
            Console.WriteLine(tc.aa);
            Console.WriteLine(tc.LongProperty);
            Console.WriteLine(tc.BoolProptrty);

            Console.ReadLine();
        }
    }

    public class TestClass
    {
        [DefaultValue("jon")]
        public string aa { get; set; }
        [DefaultValue(1)]
        public long LongProperty { get; set; }

        [DefaultValue(true)]
        public bool BoolProptrty { get; set; }

        public TestClass()
        {
            //or=>
            //foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(typeof(TestClass)))
            foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(this))
            {
                // Set default value if DefaultValueAttribute is present
                DefaultValueAttribute attr = prop.Attributes[typeof(DefaultValueAttribute)]
                                                                 as DefaultValueAttribute;
                if (attr != null)
                    prop.SetValue(this, attr.Value);
            }
        }
    }

输出效果:

jon
1
true


参考资料1改进版:http://www.codeproject.com/Articles/66073/DefaultValue-Attribute-Based-Approach-to-Property

Initialization using PropertyDescriptor.ResetValue(...)

The next method is very similar to the ComponentModel Initialization approach except it uses method ofPropertyDescriptor class called ResetValue(…). According to MSDN documentation, this method resets property to the value determined in the following order of precedence:

  1. There is a shadowed property for this property.
  2. There is a DefaultValueAttribute for this property.
  3. There is a "ResetMyProperty" method that has been implemented, where "MyProperty" is the name of the property being reset.

So the code for this implementation looks like this:

public Constructor()
{
    // Iterate through each property and call ResetValue()
    foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this))
        property.ResetValue(this);

    ...
}

This approach creates a more flexible solution as opposed to the direct querying of DefaultValue attributes. It provides alternative ways of resetting a property’s value but does not improve its performance.

Initialization with Delegates

The previous two algorithms are relatively slow. A lot of computer cycles are spent on iterating through collections and searching for correct methods to call. Their performance could be improved if all theResetValue methods were resolved only once and if their references were stored in cache for later use.

private static Action<object><this> setter;   // Reset multicast delegate

public Constructor()
{
    // Attempt to get it from cache
    if (null == setter)
    {
        // If no initializers are added do nothing
        setter = (o) => { };

        // Go through each property and add method calls to multicast delegate
        foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(this))
        {
            // Add only these which values can be reset
            if (prop.CanResetValue(this))
                setter += prop.ResetValue;
        }
    }

    // Initialize member properties
    setter(this);
}

This algorithm is similar to the previous two, but instead of calling ResetValue method on each property, it adds the method’s reference to the multicast delegate. Once all of the properties are iterated, invoking this delegate resets them to the appropriate values. Each consecutive instantiation of the class will simply call this multicast delegate without the need to rebuild it again.

This approach creates the most flexible and complete mechanism for resetting properties to their default values. Compared to other methods, it also improves their performance by eliminating unnecessary iterations through collecting the property’s descriptors and attributes. Unfortunately in terms of performance, it still does not favorably compare to the direct initialization method.

Initialization with Precompiled Setters

The solution with caching of delegates eliminates repetitive queries for the ResetValue methods. Unfortunately, it still searches for the default value for each property on every call. If neither a shadowed property, nor the "ResetMyProperty" methods are used to initialize default values, these searches could be eliminated. Initial constant value for each property can be retrieved from DefaultValue attribute only once and stored alone with the appropriate SetValue delegate in cache for further use.

By using lambda expressions, we can generate a custom code at run time, and create the following method:

(object o){ o.PropertySetter(constant); }

where o is an instance of the object, PropertySetter is a delegate to an appropriate set method, andconstant is a value retrieved from DefaultValue attribute.

List of these expressions could be compiled and added to a multicast delegate for further use.

// Default Value multicast delegate
private static Action<object><this> setter;  
     
public CompiledComponentModelInitialization()
{
    // Attempt to get it from cache
    if (null == setter)
    {
        ParameterExpression objectTypeParam = Expression.Parameter(typeof(object), 
                                                                   "this");
                                                                   
        // If no initializers are added do nothing
        setter = (o) => { };
        
        // Iterate through each property
        foreach (PropertyInfo prop in this.GetType().GetProperties(
            BindingFlags.Public | BindingFlags.Instance))
        {
            // Skip read only properties
            if (!prop.CanWrite)
                continue;
            
            // There are no more then one attribute of this type
            DefaultValueAttribute[] attr = prop.GetCustomAttributes( 
                     typeof(DefaultValueAttribute), false) as DefaultValueAttribute[];
                     
            // Skip properties with no DefaultValueAttribute
            if ((null == attr) || (null == attr[0]))
                continue;
                
            // Build the Lambda expression
            
            // Create constant expression with value from DefaultValue attribute 
            // and convert it into appropriate type
            Expression dva = Expression.Convert(Expression.Constant(attr[0].Value), 
                                                                  prop.PropertyType);
                                                                  
            // Create expression describing call to appropriate setter and 
            // passing it instance and value parameters
            Expression setExpression = Expression.Call(Expression.TypeAs(
                                     objectTypeParam, this.GetType()), 
                                     prop.GetSetMethod(), dva);
            
            // Create lambda expression describing proxy method which receives 
            // instance parameter and calls instance.setter( constant )
            Expression<Action<object><action><this>> setLambda = 
		Expression.Lambda<action><this><Action<object><action><this>>(
                                                     setExpression, objectTypeParam);
                                                     
            // Compile and add this action to multicast delegate
            setter += setLambda.Compile();
        }
    }
    
    // Initialize member properties
    setter(this);
}

The first time this class' instance is created, all of the public writeable properties are iterated through, Lambda Expressions are created, compiled and added to a multicast delegate. Next time an object of this type is instantiated, these precompiled methods are simply called through that delegate.

This approach requires considerable resources and time during initialization of its first instance, but eliminates almost all of the overhead associated with the use of DefaultValue attributes during all other instance initializations. In terms of performance, this is the only algorithm that compares very well to the direct initialization method once proxy methods are built and compiled.

using System; 
using System.Linq; 
using AOP; 

namespace YourNamespace 
{
    public class YourClass 
    { 
        public Constructor() 
        {
            this.ApplyDefaultValues(); 
        }
       ...
    } 
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值