Anatomía de las propiedades de dependencia

Análisis de propiedades de dependencia Propiedad de dependencia##

[Comprender atributos y campos]

Sabemos que los atributos son la capa que se usa para encapsular los campos en los lenguajes orientados a objetos, es como un puente entre los campos y el mundo exterior, podemos verificar la legalidad de los datos o controlar el acceso externo a través de los atributos. Cada atributo es compatible con un campo correspondiente. Incluso si es un atributo automático, el sistema creará su campo en el momento de la compilación, pero el atributo automático es solo azúcar sintáctica que nos proporciona Microsoft. En C#, el atributo finalmente se compilará en dos métodos: get_attribute name y set_attribute name (si es un atributo de solo lectura, no hay método set, de lo contrario no hay método get).

Compilado en un método, el atributo no ocupará demasiado espacio, porque el método existe en el área de métodos comunes de la memoria, y la creación de cada instancia es solo un puntero al método. Pero los campos son diferentes. Cuando se crea cada instancia, se abrirá un espacio correspondiente en la memoria para almacenar los campos. Cuantos más campos haya en una clase, mayor será el espacio que ocupará en la memoria. Después de entender esta teoría, Explicar formalmente qué es una propiedad de dependencia y por qué existe una propiedad de dependencia.

【Qué es una propiedad de dependencia】

Usamos un control, podemos ver que este control tiene muchas propiedades, y hay una sobrecarga de memoria para los campos si hay propiedades, pero de hecho, para un control, la mayoría de nosotros solo usaremos algunas de sus propiedades comunes, como Botón, usamos con mayor frecuencia Contenido, Propiedades como Altura, aquellas propiedades que no se usan con frecuencia equivalen a ocupar memoria en vano. Cuando escribimos una página XAML compleja que implica el uso de muchos controles, este desperdicio de memoria es muy grave.

En este sentido, Microsoft introdujo la propiedad de dependencia en WPF. Las propiedades de dependencia no permiten campos propios y se pueden vincular a propiedades o fuentes de datos de otros objetos a través del enlace para obtener valores. Esta dependencia está en otros objetos. El atributo anterior es la dependencia. atributo. Cuando se aclare su función, creo que todos no tendrán dudas sobre la palabra dependencia. El atributo de dependencia puede no tener su propio campo, y se puede obtener de otros objetos a través de la vinculación al usarlo. Cree temporalmente espacio de memoria para usted , por lo que no habrá consumo de memoria adicional si no la usa, pero debe tener en cuenta que si asigna un valor directamente a una propiedad de dependencia, seguirá ocupando espacio como una propiedad, y debe usar Binding para lograr la "dependencia".

Los objetos que contienen propiedades de dependencia se denominan objetos de dependencia. Este tipo de objetos deben heredar la clase base de DependencyObject. De hecho, todos los controles en WPF heredan la clase de DependencyObject. La mayoría de las propiedades en los controles son propiedades de dependencia. De esta manera , podemos vincular valores a través de Binding (los estudiantes que no estén familiarizados con Binding pueden consultar el Binding anterior (1): Data Binding Series), para que no haya desperdicio de memoria.

[Aprender las propiedades de dependencia del código]

Aprendamos cómo declarar y usar propiedades de dependencia a través del código. Mire un fragmento de código que escribí primero:

public class Pikachu : DependencyObject
{
    
    
    public static readonly DependencyProperty PikachuNameProperty =
        DependencyProperty.Register("PikachuName",typeof(string),typeof(Pikachu));
}

Como se mencionó anteriormente, el uso de propiedades de dependencia debe heredar la clase DependencyObject.Además, la declaración

Las propiedades de dependencia deben modificarse con tres modificadores públicos estáticos de solo lectura, y las propiedades de dependencia de la instancia no se obtienen a través del operador new, sino a través del método de registro de DependencyProperty.

Hay una convención para el nombre del objeto dependiente, que es usar Propiedad como sufijo. Hay muchas convenciones de nomenclatura en C#, como la interfaz tiene el prefijo I, la característica tiene el sufijo Atributo, etc. Esto es todo para una buena convención de nomenclatura Ser conocido y conocido.

◆ Paso 1: Deje que el tipo herede de la clase base DependencyObject En WPF, si observamos detenidamente la estructura del diagrama de clases del marco, encontrará que casi todos los controles de WPF heredan del tipo DependencyObject indirectamente.
◆ Paso 2: Use public static para declarar una variable > DependencyProperty. Esta variable es la propiedad de dependencia real
. Si mira el código fuente, sabrá que el principio del modo singleton simple se usa aquí para encapsular (el constructor es privado). ), y solo se llama externamente al método Register.
◆ Paso 3: Complete el registro de metadatos de la propiedad de dependencia en el constructor estático y obtenga la referencia del objeto. Mirando el código, puede ver que la propiedad de dependencia declarada ahora se coloca en un lugar similar al contenedor. Antes de explicar el principio de implementación, permítanme decir esto primero.

◆> Paso 4: en los tres pasos anteriores, hemos completado el registro de una propiedad de dependencia, entonces, ¿cómo podemos leer y escribir esta propiedad de dependencia? La respuesta es proporcionar una propiedad contenedora de instanciación de una propiedad de dependencia, a través de la cual se pueden realizar operaciones específicas de lectura y escritura.

El método Register tiene tres sobrecargas, aquí se usa la sobrecarga de sus tres parámetros, y también tiene sobrecargas de cuatro parámetros y cinco parámetros.

  • El primer parámetro es el nombre del envoltorio que especifica la propiedad de dependencia (el envoltorio se usa para envolver la propiedad de dependencia y una propiedad se usa para envolver la propiedad de dependencia para uso externo. Los detalles se discutirán a continuación y entenderemos aquí primero)
  • El segundo parámetro es para especificar el tipo de valor que será almacenado por la propiedad de dependencia El tercer parámetro es para especificar a qué clase pertenece la propiedad de dependencia, o para qué clase definir la propiedad de dependencia
  • El cuarto parámetro en otras sobrecargas son los datos de origen de la propiedad de dependencia especificada, que se utiliza para proporcionar a la persona que llama información sobre esta propiedad de dependencia. El quinto parámetro en otras sobrecargas es la devolución de llamada de verificación cuando se genera la propiedad de dependencia personalizada.

La propiedad de dependencia está declarada, pero ¿cómo asignar un valor a la propiedad de dependencia? Esto requiere los métodos en la clase base DependencyObject. Usamos el método SetValue y el método GetValue para manipular el valor de la propiedad de dependencia. Consulte lo siguiente modificado código:

public class Pikachu : DependencyObject
{
    
    
    public string PikachuName 
    {
    
    
        get => (string)GetValue(PikachuNameProperty); 
        set => SetValue(PikachuNameProperty, value); 
    }

    public static readonly DependencyProperty PikachuNameProperty =
        DependencyProperty.Register("PikachuName", typeof(string), typeof(Pikachu));
}

El código anterior es un ejemplo relativamente completo de cómo declarar una propiedad de dependencia y exponerla a través de un contenedor. La propiedad PikachuName es el contenedor de la propiedad de dependencia. En el bloque get, pase el nombre de la propiedad de dependencia a través del método GetValue para obtener el valor de la propiedad de dependencia, a través del método SetValue en el bloque Set, asigne un valor a la propiedad de dependencia, esta capa de empaquetado para la propiedad de dependencia nos facilita operar la propiedad de dependencia externamente, por lo que no sentir la existencia de la propiedad de dependencia en uso normal, porque ya sea un campo o una propiedad de dependencia, las operaciones que vemos externamente son todas sus propiedades.

Usemos un ejemplo para mostrar el uso de las propiedades de dependencia:

El código de primer plano es un control de botón denominado btn_show y el código de fondo es el siguiente:

public MainWindowBase()
{
    
    
    InitializeComponent();
    this.DataContext = this;
    Data = "我是皮卡丘";
    Pikachu pikachu = new Pikachu(); 
    //使用Binding操作类将皮卡丘对象的皮卡丘名字依赖属性关联到Data上
    BindingOperations.SetBinding(pikachu,Pikachu.PikachuNameProperty, new Binding("Data") 
    {
    
     Source = this });
    //将按钮的Content依赖属性绑定到皮卡丘的皮卡丘名字包装器上
    btn_show.SetBinding(Button.ContentProperty, new Binding(nameof(pikachu.PikachuName)) 
    {
    
     Source = pikachu });
}

La lógica de este ejemplo es tener una propiedad llamada Datos como fuente de datos. Primero, vincule la propiedad de dependencia del objeto Pikachu a la fuente de datos de Datos, y luego vincule la propiedad de dependencia de Contenido del Botón al contenedor de propiedad de dependencia de Objeto Pikachu Esto forma una cadena de unión, y el efecto de la operación es el siguiente:

En todo el proceso, solo el atributo Datos es compatible con los campos, que almacenan los datos de "Soy Pikachu". El objeto Pikachu y el objeto Botón son atributos dependientes y no ocupan espacio en la memoria. El enlace se utiliza entre ellos para formar Canal de datos, realizando así una pieza de memoria para múltiples usos. De acuerdo con el modelo de programación anterior, cada uno de Pikachu y Button necesita abrir un espacio para almacenar los datos de Data. Ahora, tres piezas de memoria se guardan en una pieza de memoria. Este es el efecto de las propiedades de dependencia en el ahorro de memoria.

[Análisis de las propiedades de dependencia del código fuente]
Analicemos por qué las propiedades de dependencia no usan nuevas instancias, sino registro, y el principio de los valores de propiedad de dependencia de operación de Get/SetValue.

Comencemos con el método Register:
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
Las sobrecargas de tres y cuatro parámetros de Register apuntan a las sobrecargas de cinco parámetros Veamos principalmente qué hay en los métodos sobrecargados de cinco parámetros. En el cuerpo del método, las primeras líneas son en realidad algunos códigos de verificación y, cuando los parámetros son incorrectos, se genera una excepción. El siguiente es un método RegisterCommon que devuelve un objeto de propiedad de dependencia. A juzgar por el nombre y el valor de retorno, este es el método central. Hagamos un seguimiento y veamos: la primera línea dentro del código usa FromNameKey para generar un objeto clave. Este FromNameKey es
inserte la descripción de la imagen aquí
un clase interna de la clase Dependencia. Su constructor necesita el nombre del contenedor entrante y el Tipo de la clase donde se encuentra el objeto dependiente. El código de esta clase y el constructor es el siguiente:
inserte la descripción de la imagen aquí

La tercera línea de código en el constructor es más importante, podemos ver que esta clase genera un código hash a través del XOR de los parámetros entrantes, después de esta operación XOR, se garantiza que se generará la misma clase y el mismo nombre de contenedor. El código hash es el mismo.

同时这个类重写了GetHashCode方法,就是把异或生成的hashcode返回出去了。

Después de comprender esta clase, regresemos a la clase RegisterCommon y luego miremos hacia abajo, el siguiente es un bloque de sincronización de subprocesos:
inserte la descripción de la imagen aquí

En este bloque de código, hay un parámetro PropertyFromName, que parece ser una colección. Encontramos la definición de esta propiedad y encontramos que es una HashTable global: entonces el significado de este bloque de código es claro. El propósito es juzgar
inserte la descripción de la imagen aquí
si la clave generada ya existe. , si existe, se lanzará una excepción y se controlará desde aquí. No se permite definir dos propiedades de dependencia con el mismo nombre de contenedor dentro de la clase. De hecho, debe ser como esto En la misma clase, las propiedades no deben tener el mismo nombre, lo mismo es cierto para las propiedades de dependencia, entonces podemos obtener otra información de aquí, es decir, PropertyFromName debe tener una gran relación con las propiedades de dependencia que queremos generar. Específicamente, sigamos mirando el código: si no hay datos de origen para las propiedades de dependencia
inserte la descripción de la imagen aquí
, el sistema generará los datos de origen predeterminados, que es una lógica de verificación. El contenido específico no se analizará aquí. interesado, puede hacer clic para verlo usted mismo, y luego llegará al núcleo del código: después de capas de controles,
inserte la descripción de la imagen aquí
atributos de dependencia Finalmente salió nuevo Después de que salió nuevo, podemos ver la sombra de PropertyFromName nuevamente
inserte la descripción de la imagen aquí
: resulta que PropertyFromName es una colección de propiedades dependientes del almacenamiento, y todos los objetos dependientes nuevos se almacenan aquí. Su código hash es el XOR de la clase FromNameKey antes.

Finalmente se devuelve la propiedad de dependencia mediante return, hasta el momento se ha resuelto todo el proceso de creación de la propiedad de dependencia.

Echemos un vistazo a la lectura del valor de la propiedad de dependencia:

Mire primero el método GetValue:
inserte la descripción de la imagen aquí
las primeras líneas de código siguen siendo verificación, y el código central es la última línea. Esto involucra la propiedad GlobalIndex de la propiedad de dependencia. Esta propiedad es obtenida por el sistema a través de una serie de algoritmos y es único. Podemos ver que este GlobalIndex se pasa al método denominado LookupEntry. Entrada significa entrada. A partir del nombre del método, podemos saber que se encuentra una entrada de acceso basada en GlobalIndex. De hecho, esta entrada es una entrada de acceso que depende de el valor del atributo.

Ingresamos al método GetValueEntry para verificar, y encontraremos un atributo llamado _effectValues, que es una matriz de tipo EffectValueEntry.Resulta que todos los valores del atributo de dependencia se almacenan en esta matriz.De acuerdo con el único GlobalIndex del atributo de dependencia, podemos obtener de El valor de la propiedad de dependencia se encuentra en esta matriz.

Veamos el método SetValue:
De hecho, si entiende GetValue, SetValue es fácil de entender. La razón es la misma. De acuerdo con el valor de GlobalIndex de la propiedad de dependencia, puede obtener la entrada y actualizar el nuevo valor. Entremos el método SetValueCommon. El código es engorroso. En realidad, hay tres piezas en el proceso:

Determine si el valor es DependencyProperty.UnsetValue, si lo es, borre el valor de la propiedad de dependencia, de modo que si queremos establecer un valor nulo para la propiedad de dependencia, no use nulo, use DependencyProperty.UnsetValue
inserte la descripción de la imagen aquí

Determine si se puede encontrar la entrada. Si no hay ninguna entrada, cree un nuevo objeto de entrada, coloque el valor en él y actualice el valor si hay una entrada. Finalmente, use el método UpdateEffectiveValue para procesar el valor
de la propiedad de dependencia
Hasta ahora, se ha analizado el proceso de lectura de la propiedad de dependencia.

Supongo que te gusta

Origin blog.csdn.net/kalvin_y_liu/article/details/122771121#comments_26934294
Recomendado
Clasificación