自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

讀 MAUI 源代碼 理解可綁定對象和可綁定屬性的存儲機(jī)制

開發(fā) 前端
在 WPF 里面,依賴屬性的提出的一部分原因是為了省內(nèi)存。在 MAUI 里面,我猜測省內(nèi)存是可綁定對象提出的一個原因。由于一個界面控件,例如按鈕等,有著非常龐大數(shù)量的屬性,假設(shè)每個控件里面的所有屬性都是需要獨立的對象不能共用,那么在復(fù)雜界面上,將會因為大量的控件的大量屬性占用大量的內(nèi)存。可綁定對象里面可以實現(xiàn)在屬性沒有被賦值時,將可以使用默認(rèn)值,而對于大部分控件來說,很多不常用的屬性都是使用默認(rèn)值

和 UWP 與 WPF 不同的是在 MAUI 里面,使用可綁定對象 BindableObject 替換了依賴對象的概念,我閱讀了 MAUI 的源代碼發(fā)現(xiàn)其實只是命名變更了,里面的機(jī)制和設(shè)計思想都是差不多的。在 MAUI 里面提供 BindableObject 用來支持可綁定屬性機(jī)制和附加屬性機(jī)制,本文將告訴大家在 MAUI 里面是如何在可綁定對象里面提供可綁定屬性和附加屬性的存儲的機(jī)制。

在 WPF 里面,依賴屬性的提出的一部分原因是為了省內(nèi)存。在 MAUI 里面,我猜測省內(nèi)存是可綁定對象提出的一個原因。由于一個界面控件,例如按鈕等,有著非常龐大數(shù)量的屬性,假設(shè)每個控件里面的所有屬性都是需要獨立的對象不能共用,那么在復(fù)雜界面上,將會因為大量的控件的大量屬性占用大量的內(nèi)存??山壎▽ο罄锩婵梢詫崿F(xiàn)在屬性沒有被賦值時,將可以使用默認(rèn)值,而對于大部分控件來說,很多不常用的屬性都是使用默認(rèn)值即可??山壎▽ο笮枰鉀Q的是讓可綁定屬性可以代替普通的 CLR 屬性,對可綁定屬性進(jìn)行賦值時,可以值和可綁定對象關(guān)聯(lián),從而可以讀取出來。既然名字叫可綁定對象,那自然也要實現(xiàn)綁定的支持,綁定的支持的核心就是通知,需要支持在屬性值變更的時候進(jìn)行通知。接下來將通過閱讀源代碼了解在 MAUI 里是如何實現(xiàn)。

打開 MAUI 的 BindableObject 的源代碼,可以看到在 BindableObject 里有 _properties 字段,定義如下:

public abstract class BindableObject : INotifyPropertyChanged, IDynamicResourceHandler
{
readonly Dictionary<BindableProperty, BindablePropertyContext> _properties = new Dictionary<BindableProperty, BindablePropertyContext>(4);
}

沒錯,這就是在 MAUI 里面的可綁定對象的存儲核心實現(xiàn)。在 MAUI 的可綁定對象里面通過 _properties? 字典存放可綁定屬性的值內(nèi)容,字典的 Key 是 BindableProperty 可綁定屬性,字典的 Value 是 BindablePropertyContext 可綁定屬性上下文,初始化字典默認(rèn)占用 4 個空間,默認(rèn)初始化空間是為了優(yōu)化而已,沒有什么特別用途。通過此字典定義可以了解到存儲的核心實現(xiàn)就是將可綁定屬性和對應(yīng)的值存入到對象的字典里,例如給某個可綁定對象的某個叫 Xxx 的可綁定屬性進(jìn)行賦值,那將會對 _properties 字典更新 Xxx 屬性的值內(nèi)容。

在 MAUI 的實現(xiàn)是,在可綁定對象里面,使用 SetValueCore 方法進(jìn)行屬性更新賦值,我刪掉了不關(guān)鍵的邏輯的代碼如下:

internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes)
{
// 獲取或創(chuàng)建可綁定屬性上下文信息
BindablePropertyContext context = GetOrCreateContext(property);

SetValueActual(property, context, value, currentlyApplying, attributes, silent);
}

BindablePropertyContext GetOrCreateContext(BindableProperty property) => GetContext(property) ?? CreateAndAddContext(property);

internal BindablePropertyContext GetContext(BindableProperty property) => _properties.TryGetValue(property, out var result) ? result : null;

BindablePropertyContext CreateAndAddContext(BindableProperty property)
{
var context = new BindablePropertyContext { ... };
_properties.Add(property, context);
return context;
}


void SetValueActual(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes, bool silent = false)
{
// 觸發(fā)對象變更前事件

context.Value = value;

// 觸發(fā)對象已變更事件
}

可以看到賦值的第一步就是調(diào)用 GetOrCreateContext 方法,嘗試去拿到上下文信息,如果拿不到就創(chuàng)建。這里的用到的 BindablePropertyContext 上下文信息是存儲可綁定屬性的關(guān)鍵,在 BindablePropertyContext 里面存放了很多字段,定義如下:

public abstract class BindableObject : INotifyPropertyChanged, IDynamicResourceHandler
{
internal class BindablePropertyContext
{
public BindableContextAttributes Attributes;
public BindingBase Binding;
public Queue<SetValueArgs> DelayedSetters;
public BindableProperty Property;
public object Value;

public bool StyleValueSet;
public object StyleValue;
}
}

可以看到 BindablePropertyContext 是一個內(nèi)部類型,也不對外開放。在 BindablePropertyContext 里面重要的就是 Value? 字段,表示存儲的實際值內(nèi)容。其次為了更好的支持綁定,也添加了 Binding 字段。

在獲取到 BindablePropertyContext 上下文之后,即可進(jìn)行賦值,賦值是調(diào)用 SetValueActual 方法進(jìn)行賦值,賦值前后分別觸發(fā)事件用來通知。觸發(fā)通知事件最重要的功能是讓綁定可以有刷新的時機(jī)。如此即可完成賦值過程。

通知事件是分別觸發(fā)可綁定的對象的通知事件和對應(yīng)的可綁定屬性的通知事件,如下面代碼:

void SetValueActual(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes, bool silent = false)
{
// 觸發(fā)對象變更前事件
property.PropertyChanging?.Invoke(this, original, value);
OnPropertyChanging(property.PropertyName);

context.Value = value;

// 觸發(fā)對象已變更事件
OnPropertyChanged(property.PropertyName);
property.PropertyChanged?.Invoke(this, original, value);
}

通過以上代碼可以看到,可綁定對象給可綁定屬性賦值的時候,就是先獲取或創(chuàng)建可綁定屬性上下文,將賦值的參數(shù)值給到 可綁定屬性上下文 的 Value 字段。如此完成賦值過程。

由于賦值的參數(shù)值被放入到 可綁定屬性上下文 的 Value 字段,而 可綁定屬性上下文 又放入到 _properties? 字典里,相當(dāng)于間接將 賦值的參數(shù)值 放入到 _properties 字典里。自然在獲取值過程里,也需要從字典里面讀取。在 MAUI 里面讀取可綁定屬性是通過 GetValue 方法實現(xiàn),代碼如下:

public object GetValue(BindableProperty property)
{
if (property == null)
throw new ArgumentNullException(nameof(property));

var context = property.DefaultValueCreator != null ? GetOrCreateContext(property) : GetContext(property);

return context == null ? property.DefaultValue : context.Value;
}

以上代碼的判斷 BindableProperty 的 DefaultValueCreator 屬性邏輯是 MAUI 特有的邏輯,和 WPF 與 UWP 不相同,咱下文再聊?;氐将@取屬性的方法上,是通過先獲取對象的可綁定上下文信息,如果能獲取到可綁定上下文,證明此可綁定對象的這個可綁定屬性曾經(jīng)被賦值過,需要用賦值更新的內(nèi)容。如果拿到的可綁定屬性上下文是空,那就使用可綁定屬性定義的默認(rèn)值即可。

在 MAUI 里面,通過 BindableProperty 的 DefaultValueCreator 屬性簡化了可綁定屬性的定義,和讓可綁定屬性更加強(qiáng)大。使用 MAUI 的可綁定屬性和可綁定對象對比 WPF 的依賴屬性和依賴對象的實現(xiàn),可以看到 MAUI 的實現(xiàn)實在簡潔很多。在 MAUI 里的 BindableProperty 的 DefaultValueCreator 屬性是一個委托,定義如下:

public sealed class BindableProperty
{
public delegate object CreateDefaultValueDelegate(BindableObject bindable);

internal CreateDefaultValueDelegate DefaultValueCreator { get; }
}

可以看到 BindableProperty 的 DefaultValueCreator 屬性的委托是支持給傳入的可綁定對象進(jìn)行處理,對可綁定對象返回特定的默認(rèn)值。這里值得說明的是,通過委托是可以特例給可綁定對象不同的默認(rèn)值的,但不代表著一定是不同的可綁定對象都一定需要不同的默認(rèn)值對象。這里只是一個委托,讓委托返回相同的對象是完全可以的。這個委托更多的是使用在判斷可綁定對象類型,根據(jù)可綁定類型對象或者狀態(tài),返回不同的默認(rèn)值?;蛘呤欠祷匾粋€需要運(yùn)行時動態(tài)計算值,而不是一個可以寫固定在代碼里面的參數(shù)。

例如對于 FontSize 的可綁定屬性的定義里,就采用讓不同的控件返回不同的默認(rèn)字體大小,定義如下:

public static readonly BindableProperty FontSizeProperty =
BindableProperty.Create("FontSize", typeof(double), typeof(IFontElement), 0d,
propertyChanged: OnFontSizeChanged,
defaultValueCreator: FontSizeDefaultValueCreator);

static object FontSizeDefaultValueCreator(BindableObject bindable)
=> ((IFontElement)bindable).FontSizeDefaultValueCreator();

也就是說對于不同的可綁定對象,獲取到的默認(rèn)的字體大小是根據(jù)對應(yīng)的可綁定對象的 FontSizeDefaultValueCreator 方法實現(xiàn)決定,不同的可綁定對象可以有不同的實現(xiàn),從而實現(xiàn)了讓默認(rèn)值關(guān)聯(lián)上具體的可綁定對象類型。這個創(chuàng)新的設(shè)計,可以省掉在 WPF 里面的大量默認(rèn)依賴屬性值重寫的邏輯代碼,省掉了這部分代碼,也可以大量減少的機(jī)制,從而減少更多的代碼。

例如 Span 和 Editor 控件對字體大小默認(rèn)值有不同的實現(xiàn)。

public class Span : GestureElement, IFontElement
{
double IFontElement.FontSizeDefaultValueCreator() =>
double.NaN;
}

public partial class Button : View, IFontElement
{
double IFontElement.FontSizeDefaultValueCreator() =>
this.GetDefaultFontSize();
}

同樣,對于某些可綁定屬性來說,需要給每個可綁定對象的對象不同的默認(rèn)值對象,例如 Grid 里面的 RowDefinitions 屬性。大家都知道,在 Grid 里面的 RowDefinitions 是一個集合,如果集合也是一個共享的默認(rèn)值,那自然會存在默認(rèn)值污染。如果默認(rèn)值是一個空值,那么將會讓 Grid 邏輯里面存在大量的判斷空邏輯,或者需要其他額外的初始化邏輯。在 MAUI 里面,通過 DefaultValueCreator 委托,實現(xiàn)了每個 Grid 對象使用獨立的默認(rèn)值對象,代碼如下:

public class Grid : Layout, IGridLayout
{
public static readonly BindableProperty RowDefinitionsProperty = BindableProperty.Create("RowDefinitions",
typeof(RowDefinitionCollection), typeof(Grid), null, validateValue: (bindable, value) => value != null,
propertyChanged: UpdateSizeChangedHandlers, defaultValueCreator: bindable =>
{
// 每個 Grid 對象使用獨立的,新創(chuàng)建的默認(rèn)值對象
var rowDef = new RowDefinitionCollection();
rowDef.ItemSizeChanged += ((Grid)bindable).DefinitionsChanged;
return rowDef;
});
}

在 MAUI 里面除了可綁定屬性之外,還有一個特殊的屬性類型,附加屬性。附加屬性可以定義在任意的類型里面,通過附加屬性,給某個現(xiàn)有的類型附加上屬性。功能上和 WPF 或 UWP 的附加屬性功能是相同的??山壎▽傩院透郊訉傩远际窍嗤?BindableProperty 類型,只是在創(chuàng)建的時候,調(diào)用的靜態(tài)創(chuàng)建方法不同而已。對于可綁定屬性來說,調(diào)用的是 BindableProperty.Create? 方法創(chuàng)建。對于附加屬性來說,調(diào)用 BindableProperty.CreateAttached 創(chuàng)建。在 MAUI 里面,通過閱讀代碼,我認(rèn)為分開兩個方法更多的是為了兼容 WPF 或 UWP 的寫法,沒有非常本質(zhì)的差別,參數(shù)也差不多,如下面代碼:

internal static BindableProperty Create(string propertyName, Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode, ValidateValueDelegate validateValue,
BindingPropertyChangedDelegate propertyChanged, BindingPropertyChangingDelegate propertyChanging, CoerceValueDelegate coerceValue, BindablePropertyBindingChanging bindingChanging,
CreateDefaultValueDelegate defaultValueCreator = null)
{
return new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, bindingChanging,
defaultValueCreator: defaultValueCreator);
}

internal static BindableProperty CreateAttached(string propertyName, Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode, ValidateValueDelegate validateValue,
BindingPropertyChangedDelegate propertyChanged, BindingPropertyChangingDelegate propertyChanging, CoerceValueDelegate coerceValue, BindablePropertyBindingChanging bindingChanging,
bool isReadOnly, CreateDefaultValueDelegate defaultValueCreator = null)
{
return new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, bindingChanging, isReadOnly,
defaultValueCreator);
}

如此可以看到可綁定屬性和附加屬性從參數(shù)上是似乎相同的。由于附加屬性也是一個可綁定屬性類型,同理可以了解到附加屬性的存儲也和可綁定對象的可綁定屬性的存儲是相同的。如此也能解答一個問題,在 MAUI 的附加屬性,附加到對象上,附加屬性的參數(shù)值是如何跟隨對象的生命周期的問題。由于附加屬性也是一個可綁定屬性,同樣將參數(shù)值存在可綁定對象的 _properties? 字典里面,在對象會 GC 回收時,自然 _properties 字段也被回收,那放在字典里面的參數(shù)值也自然被減去引用,當(dāng)參數(shù)值的沒有被引用時,也就自然被回收。

在 MAUI 里面,可綁定對象基類型的意義就是提供了可綁定屬性的機(jī)制,存儲可綁定屬性的方式就是通過 _properties 字典存放。通過字典存放的內(nèi)容是被賦值更改的屬性,沒有賦值更改的屬性是沒有被放入到字典里面,獲取在字典里面沒有存放的屬性時,將會通過對應(yīng)的可綁定屬性獲取到默認(rèn)值。默認(rèn)值的獲取有兩個方式,一個是可綁定屬性的固定的默認(rèn)值屬性,另一個是通過可綁定屬性的默認(rèn)值創(chuàng)建委托創(chuàng)建默認(rèn)值。在 MAUI 里的可綁定屬性的默認(rèn)值創(chuàng)建委托是一個創(chuàng)新,可以寫出讓不同的可綁定對象使用不同的默認(rèn)值的功能,也可以寫出根據(jù)不同的可綁定對象類型返回不同的默認(rèn)值,通過委托的方式靈活實現(xiàn)復(fù)雜的功能。

責(zé)任編輯:武曉燕 來源: 分布式應(yīng)用運(yùn)行時
相關(guān)推薦

2012-01-09 11:26:15

Java

2010-06-22 15:31:22

autoconf安裝

2016-12-14 14:29:30

Java動態(tài)綁定機(jī)制

2010-08-13 14:19:44

Flex綁定機(jī)制

2010-08-06 10:15:35

Flex綁定

2010-07-30 10:45:08

Flex數(shù)據(jù)綁定

2021-10-08 07:53:01

Go 尋址元素

2022-03-11 11:51:16

自動駕駛智能技術(shù)

2018-05-10 11:57:12

2024-02-26 07:26:27

RustC++開發(fā)

2021-12-09 05:36:16

云存儲可擴(kuò)展性數(shù)據(jù)存儲云存儲

2016-11-30 18:35:03

JavaScript

2009-08-13 15:40:29

C#數(shù)據(jù)綁定控件

2023-11-08 13:55:27

2021-08-08 08:23:45

SQL代碼編程

2014-12-29 10:19:01

Java

2021-10-18 11:05:51

SQL源代碼AddUpdate

2017-01-05 19:29:10

公共云云存儲微軟

2023-10-20 09:51:00

編程開發(fā)

2018-06-27 08:30:51

HDD密度存儲
點贊
收藏

51CTO技術(shù)棧公眾號