語言

相依性屬性的安全性

透過 Windows Presentation Foundation (WPF) 屬性系統,讀寫相依性屬性的可存取性實際上使它們成為公用屬性。 因此,無法對讀寫相依性屬性值進行安全性保證。 WPF 屬性系統為唯讀相依性屬性提供更多安全性,以便限制寫入存取。

屬性包裝器的存取與安全性

通用語言執行平台 (CLR) 屬性包裝函式通常包含在讀寫相依性屬性實作中,以簡化取得或設定屬性值。 若有包含,CLR 屬性包裝器是一種便利方法,它實作與基礎相依性屬性互動的 GetValue 和 SetValue 靜態呼叫。 基本上,CLR 屬性包裝函式會將相依性屬性公開為相依性屬性支援的 CLR 屬性,而不是私人欄位。

套用安全性機制並限制對 CLR 屬性包裝器的存取可能會防止使用便利方法,但這些技巧不會防止直接呼叫 GetValue 或 SetValue。 換句話說,一律可透過 WPF 屬性系統存取讀寫相依性屬性。 如果您要實作讀寫相依性屬性,請避免限制對 CLR 屬性包裝函式的存取。 相反地,將 CLR 屬性包裝函式宣告為公用成員,讓呼叫端知道相依性屬性的真正存取層級。

屬性系統的相依屬性呈現

WPF 屬性系統透過其 DependencyProperty 識別項提供可讀寫的相依性屬性的存取權。 此識別碼可在 GetValue 和 SetValue 呼叫中使用。 即使靜態識別碼欄位非公用,屬性系統的數個層面也會傳回 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

另請參閱