How does the dependency attribute of WPF save memory?

Source: Internet
Author: User

 

WPF upgraded the property system of CLR and added dependency and additional attributes. The use of dependency attributes has many advantages, of which two are the most eye-catching:

1) saves memory overhead;

2) The attribute value can be dependent on other objects through Binding, so that all the dependency attributes of the data source depend on this data source for update.

Second, if you have developed a WPF or SilverLight application, you can feel the benefits it brings very quickly, in terms of memory saving, we cannot deeply feel the psychological coolness it brings. I try to briefly explain how dependency attributes save memory for us.

Let's first take a look at the traditional CLR attributes. First, we define the individual class Person, which is simple and only contains the Name and Coutry attributes. Country is China by default.

Public class Person

{

Public Person ()

{

Country = "China ";

}

Public string Name {get; set ;}

Public string Country {get; set ;}

 

}

  

If we need to create 10000 Chinese now, it would be too simple to instantiate 10000 persons in a loop. If you do not care about the memory consumption of the program, you will certainly not feel heartbroken. Otherwise, you will shout out, because the Country of the 10000 Chinese people is China, but the memory must open up space for each China to store. This is really a storm! Well, here you should know that the dependency attribute can be highlighted by memory saving, although we are the best at the one-to-one change feature mentioned above.

The Dependency Property DependencyProperty cannot be used. The Dependency object DependencyObject must be described from the two methods provided by Dependency, GetValue and SetValue.

Public class DependencyObject: DispatcherObject

 

{

 

Public object GetValue (DependencyProperty dp)

 

{

 

    

 

}

 

Public void SetValue (DependencyProperty dp, object value)

 

{

 

}

 

}

In the past, DependencyProperty itself did not provide operations for obtaining and setting dependency attribute values, but was the responsibility of DependencyObject. DependencyObject uses the GetValue and SetValue methods to read and save attribute values through the DependencyProperty instance. Note that the read and save attribute values here are actually the property values corresponding to DependencyProperty, that is, the value of the second parameter object type of the SetValue method, which is different from the DependencyProperty instance object itself. If you still cannot understand it well, it doesn't matter. Continue to read it and you will understand it. Next we will upgrade the Country attribute of Person to make it a true dependency attribute.

Public class Person: DependencyObject

{

/// <Summary>

/// Dependency attributes

/// </Summary>

Public static readonly DependencyProperty CountryProperty = DependencyProperty. Register ("Country", typeof (string), typeof (Person), new PropertyMetadata ("China"), new ValidateValueCallback (CountryValidateValueCallback ));

 

/// <Summary>

/// CLR property package for dependency properties

/// </Summary>

Public string Country

{

Get {return (string) GetValue (CountryProperty );}

Set {SetValue (CountryProperty, value );}

}

 

/// <Summary>

/// Property value verification

/// </Summary>

/// <Param name = "value"> </param>

/// <Returns> </returns>

Public static bool CountryValidateValueCallback (object value)

{

String country = (string) value;

If (country. Equals ("Japan") return false;

Return true;

}

 

/// <Summary>

/// Name is a CLR attribute

/// </Summary>

Public string Name {get; set ;}

}

The Dependency Property CountryProperty created above uses DependencyProperty. the reload method of Register's most complete parameters. Details about each parameter in this method are not the focus of this Article. If you need to learn more, read the article about dependency attributes written by Clingingboy. The DependencyProperty. Register parameter "Country" is used to specify which CLR attribute is used as the package of this dependency attribute. In addition, you need to use this HashCode, which will be discussed later. The second parameter "typeof (string)" indicates the type of value stored in this dependency attribute. The third parameter "typeof (Person)" is used to specify the type of the dependent attribute host, and its HashCode is also required. The fourth parameter "new PropertyMetadata (" China ")" can specify the default value of the read value of the dependency attribute. The default value is "China". The fifth parameter is "new ValidateValueCallback (CountryValidateValueCallback )", it specifies the method for verifying the value. Well, we will exclude Japan as the method for verifying the value. Any Country value assigned to Japan will throw an exception.

The CountryProperty defined here is a static object. We know that the values in the static object are all changed, that is, I modified the static object in one place, and all the places that reference this static object will change, this indicates that CountryProperty is not suitable for storing the value attribute. Otherwise, if I instantiate 10000 persons, all these 10000 persons share one CountryProperty, which Person object is used to save the Country?

The previous instantiation of A DependencyProperty does not use the new Keyword, but uses the DependencyProperty. Register static method. This static method has many advantages. Next we will analyze this static method.

Let's take a look at the source code of DependencyProperty. Register in the DependencyProperty class. Here we will remove some of the Code that interferes with our reading for ease of reading:

Public static DependencyProperty Register (string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback)

{

RegisterParameterValidation (name, propertyType, ownerType );

DependencyProperty property = RegisterCommon (name, propertyType, ownerType, defaultMetadata, validateValueCallback );

Return property;

}

The RegisterParameterValidation (name, propertyType, ownerType) method verifies whether the first, second, and third parameters are null. If any one is null, an exception is thrown, it indicates that these three parameters are required to call the Register method.

DependencyProperty property = RegisterCommon (name, propertyType, ownerType, defaultMetadata, validateValueCallback). This line tells us that the RegisterCommon method is used internally to instantiate the DependencyProperty object. Let's take a look at the key source code of the RegisterCommon method, and only the key source code is retained:

Private static DependencyProperty RegisterCommon (string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)

{

FromNameKey key = new FromNameKey (name, ownerType );

Lock (Synchronized)

{

If (PropertyFromName. Contains (key ))

{

Throw new ArgumentException (SR. Get (SRID. PropertyAlreadyRegistered, name, ownerType. Name ));

}

}

 

// Establish default metadata for all types, if none is provided

If (defametametadata = null)

{

DefaultMetadata = AutoGeneratePropertyMetadata (propertyType, validateValueCallback, name, ownerType );

}

// Create property

DependencyProperty dp = new DependencyProperty (name, propertyType, ownerType, defaultMetadata, validateValueCallback );

 

// Map owner type to this property

// Build key

Lock (Synchronized)

{

PropertyFromName [key] = dp;

}

 

 

Return dp;

}

If (defametametadata = null)

{

DefaultMetadata = AutoGeneratePropertyMetadata (propertyType, validateValueCallback, name, ownerType );

}

  

From this code, we can know that when the fourth parameter of DependencyProperty. Register is null, a default metadata will be created.

PropertyFromName [key] = dp; this code tells us that the created DependencyProperty object is kept in the global Hashtable named PropertyFromName.

Private static Hashtable PropertyFromName = new Hashtable ();

Then how does this key come from? Return to the first sentence of the source code: FromNameKey key = new FromNameKey (name, ownerType). We can know that the key object type in PropertyFromName is FromNameKey, we know whether the key of a Hashtable element is the same and whether the HashCode of the key is the same. Therefore, we need to check what is returned by GetHashCode of FromNameKey:

Private class FromNameKey

{

Public FromNameKey (string name, Type ownerType)

{

_ Name = name;

_ OwnerType = ownerType;

 

_ HashCode = _ name. GetHashCode () ^ _ ownerType. GetHashCode ();

}

 

Public void UpdateNameKey (Type ownerType)

{

_ OwnerType = ownerType;

 

_ HashCode = _ name. GetHashCode () ^ _ ownerType. GetHashCode ();

}

 

Public override int GetHashCode ()

{

Return _ hashCode;

}

}

Now it's clear. We mentioned DependencyProperty. the HachCode of the first parameter "Country" and third parameter typeof (Person) of Register must be used, here, the HashCode value of FromNameKey is obtained by ^ calculation of the HashCode of the first parameter and the HashCode of the third parameter typeof (Person. As a result, we can infer that the same Dependency of A type will only save one instance object in the global PropertyFromName (because the key-value pairs of Hashtable cannot have the same key ).

Through the above analysis, we can summarize the role of DependencyProperty. Register:

1) store a DependencyProperty object in a global Hashtable (PropertyFromName ), the Key of the Hashtable object is determined by the CLR property package name corresponding to the dependency property object and the type of DHashCode that stores the Dependency Property object.

2) The DependencyProperty object saves the default value of an attribute metadata;

3) returns a DependencyProperty object instance.

The DependencyProperty object only saves a default value. When we call the SetValue (DependencyProperty dp, object value) method of DependencyObject to save the property value, where is the second parameter value saved? This requires reading the source code of the DependencyObject class. After reading the source code, we find that the DependencyObject class has a private array variable:

Private EffectiveValueEntry [] _ effectiveValues;

When we call the DependencyObject's GetValue (DependencyProperty dp) method, the DependencyProperty object's GlobalIndex attribute determines whether javastivevalueentry [] contains the property value of this dependency property object, if no value exists, the default value of the dependency attribute is returned. Understanding this is critical. This is one of the key to saving memory for dependency properties, because the dependency attribute objects in a class are static, that is, all instantiated objects are public dependent attribute objects, back to our starting topic. When you instantiate 10000 persons, if the SetValue method of Person is not called, when you read the Country attribute of these 10000 persons, they are all read from the default value of CountryProperty of a dependency property object. In this case, only one address in the memory can be used to store "China", greatly saving the memory overhead.

When we call the SetValue (DependencyProperty dp, object value) method of DependencyObject, this value will be saved to an element of the EffectiveValueEntry [] EffectiveValueEntry type.

In WPF, most UI controls have a long inheritance system. The attributes inherited by a control have a basket. In addition, if the Dependency Property is defined in the parent class of the control, all subclass objects of the parent class share the Dependency Property object. Therefore, the use of the dependency property greatly reduces the memory consumption of the control.

I hope this article will help you understand the memory saving mechanism of dependency attributes in WPF. The author's level is limited. If not, please be a master.

Postscript: After writing, I think of the string type in. NET, that is, string a = "China" and string B = "China" variables a and B actually point to the same address. In the Person class in this article, the Data Type stored by CountryProperty is a string, because. NET special optimizations for string, so defining a storage string type DependencyProperty is not suitable for saving memory. If I create 10000 Person objects, the advantage of using dependency properties is that it saves the address referenced by the address pointing to the "China" address. In this case, it still has the advantage of saving memory, however, it is more appropriate to use other types such as List <Int> to write this article.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.