Segurança de propriedade de dependência (WPF .NET)

A acessibilidade das propriedades de dependência de leitura/gravação por meio do sistema de propriedades do Windows Presentation Foundation (WPF) efetivamente as torna propriedades públicas. Como resultado, não é possível fazer garantias de segurança sobre valores de propriedade de dependência de leitura/gravação. O sistema de propriedades do WPF fornece mais segurança para propriedades de dependência somente leitura para que você possa restringir o acesso de gravação.

Acesso e segurança de wrappers de propriedade

Um wrapper de propriedade CLR (Common Language Runtime) geralmente é incluído em implementações de propriedade de dependência de leitura/gravação para simplificar a obtenção ou configuração de valores de propriedade. Se incluído, o wrapper de propriedade CLR é um método de conveniência que implementa as GetValue chamadas estáticas e SetValue que interagem com a propriedade de dependência subjacente. Essencialmente, um wrapper de propriedade CLR expõe uma propriedade de dependência como uma propriedade CLR apoiada por uma propriedade de dependência em vez de um campo privado.

A aplicação de mecanismos de segurança e a restrição do acesso ao wrapper de propriedade CLR podem impedir o uso do método de conveniência, mas essas técnicas não impedirão chamadas diretas para GetValue ou SetValue. Em outras palavras, uma propriedade de dependência de leitura/gravação está sempre acessível por meio do sistema de propriedades do WPF. Se você estiver implementando uma propriedade de dependência de leitura/gravação, evite restringir o acesso ao wrapper de propriedade CLR. Em vez disso, declare o wrapper de propriedade CLR como um membro público para que os chamadores estejam cientes do verdadeiro nível de acesso da propriedade de dependência.

Exposição do sistema de propriedades de dependência

O sistema de propriedades do WPF fornece acesso a uma propriedade de dependência de leitura/gravação por meio de seu DependencyProperty identificador. O identificador pode ser usado e GetValue SetValue chama. Mesmo que o campo identificador estático não seja público, vários aspectos do sistema de propriedades retornarão um DependencyProperty como ele existe em uma instância de uma classe ou classe derivada. Por exemplo, o GetLocalValueEnumerator método retorna identificadores para instâncias de propriedade de dependência com um valor definido localmente. Além disso, você pode substituir o OnPropertyChanged método virtual para receber dados de evento que relatarão o DependencyProperty identificador para propriedades de dependência que mudaram de valor. Para conscientizar os chamadores sobre o verdadeiro nível de acesso de uma propriedade de dependência de leitura/gravação, declare seu campo identificador como um membro público.

Observação

Embora declarar um campo de identificador de propriedade de dependência como private reduza o número de maneiras pelas quais uma propriedade de dependência de leitura/gravação é acessível, a propriedade não será privada de acordo com a definição de linguagem CLR.

Segurança de validação

Aplicar um Demand a a ValidateValueCallback e esperar que a validação falhe em caso Demand de falha não é um mecanismo de segurança adequado para restringir as alterações de valor de propriedade. Além disso, a invalidação de novo valor imposta por meio pode ValidateValueCallback ser suprimida por chamadores mal-intencionados, se esses chamadores estiverem operando no domínio do aplicativo.

Acesso a propriedades de dependência somente leitura

Para restringir o acesso, registre sua propriedade como uma propriedade de dependência somente leitura chamando o RegisterReadOnly método. O RegisterReadOnly método retorna um DependencyPropertyKey, que você pode atribuir a um campo de classe não público. Para propriedades de dependência somente leitura, o sistema de propriedades do WPF fornecerá acesso de gravação apenas para aqueles que têm uma referência ao DependencyPropertyKey. Para ilustrar esse comportamento, o seguinte código de teste:

  • Instancia uma classe que implementa propriedades de dependência de leitura/gravação e somente leitura.
  • Atribui um private modificador de acesso a cada identificador.
  • Implementa get apenas acessadores.
  • Usa o GetLocalValueEnumerator método para acessar as propriedades de dependência subjacentes por meio do sistema de propriedades do WPF.
  • Chamadas GetValue e SetValue para testar o acesso a cada valor de propriedade de dependência.
    /// <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

Confira também