相依性屬性安全性 (WPF .NET)
透過 Windows Presentation Foundation (WPF) 屬性系統的讀寫相依性屬性的輔助功能,有效地使其成為公用屬性。 因此,無法對讀寫相依性屬性值進行安全性保證。 WPF 屬性系統為唯讀相依性屬性提供更多安全性,以便限制寫入存取。
屬性包裝函式的存取和安全性
Common Language Runtime (CLR) 屬性包裝函式通常包含在讀寫相依性屬性實作中,以簡化取得或設定屬性值。 如果包含,CLR 屬性包裝函式是一種便利方法,可實 GetValue 作與基礎相依性屬性互動的和 SetValue 靜態呼叫。 基本上,CLR 屬性包裝函式會將相依性屬性公開為依存性屬性支援的 CLR 屬性,而不是私用字段。
套用安全性機制並限制對 CLR 屬性包裝函式的存取可能會防止使用便利方法,但這些技術不會防止直接呼叫 GetValue
或 SetValue
。 換句話說,一律可透過 WPF 屬性系統存取讀寫相依性屬性。 如果您要實作讀寫相依性屬性,請避免限制 CLR 屬性包裝函式的存取。 相反地,將 CLR 屬性包裝函式宣告為公用成員,讓呼叫端知道相依性屬性的真正存取層級。
相依性屬性的屬性系統暴露
WPF 屬性系統透過其 DependencyProperty 識別碼提供讀取寫入相依性屬性的存取權。 標識碼可在和 SetValue 呼叫中使用GetValue。 即使靜態標識符欄位非公用,屬性系統的數個層面也會傳回 DependencyProperty
,因為它存在於類別或衍生類別的實例上。 例如, GetLocalValueEnumerator 方法會傳回具有本機設定值之相依性屬性實例的標識符。 此外,您可以覆寫 OnPropertyChanged 虛擬方法,以接收事件數據,以報告 DependencyProperty
已變更值的相依性屬性標識碼。 若要讓呼叫端知道讀寫相依性屬性的真正存取層級,請將其標識符欄位宣告為公用成員。
注意
雖然宣告相依性屬性標識符字段會 private
減少可存取讀寫相依性屬性的方式數目,但屬性不會 根據CLR語言定義私用 。
驗證安全性
將 Demand 套用至 ValidateValueCallback ,且預期驗證在失敗時 Demand
失敗,不是限制屬性值變更的適當安全性機制。 此外,如果這些呼叫端在應用程式域內運作,透過強制執行 ValidateValueCallback
的新值失效可能會由惡意呼叫端隱藏。
存取唯讀相依性屬性
若要限制存取,請呼叫 RegisterReadOnly 方法,將您的屬性註冊為只讀相依性屬性。 方法 RegisterReadOnly
會傳 DependencyPropertyKey回 ,您可以指派給非公用類別欄位。 若為只讀相依性屬性,WPF 屬性系統只會提供具有 參考 DependencyPropertyKey
之人的寫入許可權。 為了說明此行為,下列測試程序代碼:
- 具現化實作讀寫和唯讀相依性屬性的類別。
- 將
private
存取修飾詞指派給每個標識符。 - 只實作
get
存取子。 - GetLocalValueEnumerator使用方法來透過 WPF 屬性系統存取基礎相依性屬性。
- 呼叫 GetValue 和 SetValue 以測試每個相依性屬性值的存取權。
/// <summary>
/// Test get/set access to dependency properties exposed through the WPF property system.
/// </summary>
public static void DependencyPropertyAccessTests()
{
// Instantiate a class that implements read-write and read-only dependency properties.
Aquarium _aquarium = new();
// Access each dependency property using the LocalValueEnumerator method.
LocalValueEnumerator localValueEnumerator = _aquarium.GetLocalValueEnumerator();
while (localValueEnumerator.MoveNext())
{
DependencyProperty dp = localValueEnumerator.Current.Property;
string dpType = dp.ReadOnly ? "read-only" : "read-write";
// Test read access.
Debug.WriteLine($"Attempting to get a {dpType} dependency property value...");
Debug.WriteLine($"Value ({dpType}): {(int)_aquarium.GetValue(dp)}");
// Test write access.
try
{
Debug.WriteLine($"Attempting to set a {dpType} dependency property value to 2...");
_aquarium.SetValue(dp, 2);
}
catch (InvalidOperationException e)
{
Debug.WriteLine(e.Message);
}
finally
{
Debug.WriteLine($"Value ({dpType}): {(int)_aquarium.GetValue(dp)}");
}
}
// Test output:
// Attempting to get a read-write dependency property value...
// Value (read-write): 1
// Attempting to set a read-write dependency property value to 2...
// Value (read-write): 2
// Attempting to get a read-only dependency property value...
// Value (read-only): 1
// Attempting to set a read-only dependency property value to 2...
// 'FishCountReadOnly' property was registered as read-only
// and cannot be modified without an authorization key.
// Value (read-only): 1
}
}
public class Aquarium : DependencyObject
{
public Aquarium()
{
// Assign locally-set values.
SetValue(FishCountProperty, 1);
SetValue(FishCountReadOnlyPropertyKey, 1);
}
// Failed attempt to restrict write-access by assigning the
// DependencyProperty identifier to a non-public field.
private static readonly DependencyProperty FishCountProperty =
DependencyProperty.Register(
name: "FishCount",
propertyType: typeof(int),
ownerType: typeof(Aquarium),
typeMetadata: new PropertyMetadata());
// Successful attempt to restrict write-access by assigning the
// DependencyPropertyKey to a non-public field.
private static readonly DependencyPropertyKey FishCountReadOnlyPropertyKey =
DependencyProperty.RegisterReadOnly(
name: "FishCountReadOnly",
propertyType: typeof(int),
ownerType: typeof(Aquarium),
typeMetadata: new PropertyMetadata());
// Declare public get accessors.
public int FishCount => (int)GetValue(FishCountProperty);
public int FishCountReadOnly => (int)GetValue(FishCountReadOnlyPropertyKey.DependencyProperty);
}
''' <summary>
''' ' Test get/set access to dependency properties exposed through the WPF property system.
''' </summary>
Public Shared Sub DependencyPropertyAccessTests()
' Instantiate a class that implements read-write and read-only dependency properties.
Dim _aquarium As New Aquarium()
' Access each dependency property using the LocalValueEnumerator method.
Dim localValueEnumerator As LocalValueEnumerator = _aquarium.GetLocalValueEnumerator()
While localValueEnumerator.MoveNext()
Dim dp As DependencyProperty = localValueEnumerator.Current.[Property]
Dim dpType As String = If(dp.[ReadOnly], "read-only", "read-write")
' Test read access.
Debug.WriteLine($"Attempting to get a {dpType} dependency property value...")
Debug.WriteLine($"Value ({dpType}): {CInt(_aquarium.GetValue(dp))}")
' Test write access.
Try
Debug.WriteLine($"Attempting to set a {dpType} dependency property value to 2...")
_aquarium.SetValue(dp, 2)
Catch e As InvalidOperationException
Debug.WriteLine(e.Message)
Finally
Debug.WriteLine($"Value ({dpType}): {CInt(_aquarium.GetValue(dp))}")
End Try
End While
' Test output
' Attempting to get a read-write dependency property value...
' Value (read-write): 1
' Attempting to set a read-write dependency property value to 2...
' Value (read-write): 2
' Attempting to get a read-only dependency property value...
' Value (read-only): 1
' Attempting to set a read-only dependency property value to 2...
' 'FishCountReadOnly' property was registered as read-only
' and cannot be modified without an authorization key.
' Value (read-only): 1
End Sub
End Class
Public Class Aquarium
Inherits DependencyObject
Public Sub New()
' Assign locally-set values.
SetValue(FishCountProperty, 1)
SetValue(FishCountReadOnlyPropertyKey, 1)
End Sub
' Failed attempt to restrict write-access by assigning the
' DependencyProperty identifier to a non-public field.
Private Shared ReadOnly FishCountProperty As DependencyProperty =
DependencyProperty.Register(
name:="FishCount",
propertyType:=GetType(Integer),
ownerType:=GetType(Aquarium),
typeMetadata:=New PropertyMetadata())
' Successful attempt to restrict write-access by assigning the
' DependencyPropertyKey to a non-public field.
Private Shared ReadOnly FishCountReadOnlyPropertyKey As DependencyPropertyKey =
DependencyProperty.RegisterReadOnly(
name:="FishCountReadOnly",
propertyType:=GetType(Integer),
ownerType:=GetType(Aquarium),
typeMetadata:=New PropertyMetadata())
' Declare public get accessors.
Public ReadOnly Property FishCount As Integer
Get
Return GetValue(FishCountProperty)
End Get
End Property
Public ReadOnly Property FishCountReadOnly As Integer
Get
Return GetValue(FishCountReadOnlyPropertyKey.DependencyProperty)
End Get
End Property
End Class