依赖属性安全性

通过 Windows Presentation Foundation (WPF) 属性系统实现读写依赖属性的可访问性实际上使它们成为公共属性。 因此,无法对读写依赖项属性值进行安全保证。 WPF 属性系统为只读依赖属性提供更多的安全性,以便限制写入访问。

属性包装器的访问和安全性

公共语言运行时 (CLR) 属性包装器通常包含在读写依赖属性实现中,以简化获取或设置属性值。 如果包含,则 CLR 属性包装器是一种方便的方法,用于实现与基础依赖属性交互的静态调用 GetValueSetValue。 实质上,CLR 属性包装器将依赖属性公开为 CLR 属性,而该 CLR 属性由依赖属性支持,而非专用字段。

应用安全机制并限制对 CLR 属性包装器的访问可能会阻止使用便利方法,但这些技术不会阻止直接调用 GetValueSetValue。 换句话说,始终可通过 WPF 属性系统访问读写依赖属性。 如果要实现读写依赖属性,请避免限制对 CLR 属性包装器的访问。 相反,请将 CLR 属性包装器声明为公共成员,以便调用方知道依赖属性的真实访问级别。

依赖属性在属性系统中的暴露

WPF 属性系统通过其 DependencyProperty 标识符提供对读写依赖属性的访问权限。 标识符在调用中GetValueSetValue可用。 即使静态标识符字段是非公共的,属性系统的几个方面仍会返回它在类或派生类实例中的原样。 例如,该方法 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

另请参阅