Default Value Attribute Based Approach to Property Initializton

20255 ワード

参考資料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 PropertyDescriptore.ReetValue(…)
The next method is very simiar to the  ComponentModel Initialization aproach except it uses method ofPropertyDescriptor クラスcaled ReetValue(…).Acctording to MSDN documentation、this method reets property to the value determined in the follwing order of precedence:
The re is a showed property for this propertyThe re is a  DefaultValueAttribute for this propertyThe re is a「ResetMyProperty」method has been implemented、where「MyProperty」is the property being reet.So the code for this implemention looks like this:

 Collappse
 |  Copyコード
public Constructor()
{
    // Iterate through each property and call ResetValue()
    foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this))
        property.ResetValue(this);

    ...
}
This aproach creates a more flexible solution as opposed to the direct querying of  DefaultValue atributes.It provides alternative ways of reetting a property s value but does not improve its performance.
Initialization with Delegates
The previous two algorithms are relatively slow.A lot of computter cycles are spent on iterating through collection and searching for corect methods to call.The ir performance could proved proved alive methods were rereved only once and if their references were stored in cache for later use.

 Collappse
 |  Copyコード
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 simiar to the previous two、but instead of caling  ResetValue method on each property,it adds the method’s reference to the multiast delegate.Onece all of the properties arit,invoking this delettes the m to the mut the propris values.Each consecutive instantiation of
This appech creates the most freexible and coplete mechans for reetting properties to their default values.Compred to other methmans,it also improves their performance by elimination necessarititationit still does not favorably compre to the direct initialization method.
Initialization with Prcompled Setters
The solution with caching of delegates eliminates repetive queries for the  ResetValue methods.Unfortunally,it still searches for the default value for each property on evercal.If neither a showed property,nor the vatrate“ResetValue”methods ares to initiaze default values,thesearemberge properchect  ResetMyProperty atribute only once and stored alone with the approprate  DefaultValue delegate in cache for further use.
By using lamda expressions、we can generate a custom code at run time、and create the following method:SetValuewhere  (object o){ o.PropertySetter(constant); } is an instance of the object,  o is a delegate to an approprate set method、andPropertySetter is a value retrieved from  constant atribute.
List of these expressions could be compled and added to a multicast delegate for further use.

 Collappse
 |  Copyコード
// 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  DefaultValue writeable properties art iterated through、Lamda Expressions are created、compled and added to a multiast delegate.Next time abject of this type is instantiated、these precompiled methods aritits are.
This aproach requires considersable reource and time during initiazation of its first instance、but eliminates all of the overhead assited with the use of  public atributes during all other instance initializations.In terms of performance,this the only algorithm that compres very well to the direct initialization methode proxy methods and compled.
using System; 
using System.Linq; 
using AOP; 

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