相依性屬性安全性 (WPF .NET)

透過 Windows Presentation Foundation (WPF) 屬性系統的讀寫相依性屬性的協助工具,有效地使其成為公用屬性。 因此,無法對讀寫相依性屬性值進行安全性保證。 WPF 屬性系統為唯讀相依性屬性提供更多安全性,以便限制寫入存取。

重要

.NET 7 和 .NET 6 的桌面指南檔正在建置中。

屬性包裝函式的存取和安全性

Common Language Runtime (CLR) 屬性包裝函式通常包含在讀寫相依性屬性實作中,以簡化取得或設定屬性值。 如果包含,CLR 屬性包裝函式是一種便利方法,可實 GetValue 作與基礎相依性屬性互動的 和 SetValue 靜態呼叫。 基本上,CLR 屬性包裝函式會將相依性屬性公開為依存性屬性支援的 CLR 屬性,而不是私用欄位。

套用安全性機制並限制對 CLR 屬性包裝函式的存取可能會防止使用便利方法,但這些技術不會防止直接呼叫 GetValueSetValue 。 換句話說,一律可透過 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 屬性系統存取基礎相依性屬性。
  • 呼叫 GetValueSetValue 以測試每個相依性屬性值的存取權。
    /// <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

另請參閱