Безопасность свойства зависимости (WPF .NET)

Доступность чтения и записи свойств зависимостей через систему свойств Windows Presentation Foundation (WPF) фактически делает их открытыми свойствами. В результате невозможно гарантировать безопасность значений свойств зависимостей, доступных для чтения и записи. Система свойств WPF обеспечивает более высокий уровень безопасности для свойств зависимостей, доступных только для чтения, так что вы можете ограничить доступ для записи.

Важно!

Документация по рабочему столу для .NET 7 и .NET 6 находится в стадии разработки.

Доступ и безопасность оболочек свойств

Оболочка свойства среды 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

См. также