Compartilhar via


Segurança da propriedade de dependência

A acessibilidade das propriedades de dependência de leitura/gravação por meio do sistema de propriedades do WPF (Windows Presentation Foundation) efetivamente as torna propriedades públicas. Como resultado, não é possível garantir a segurança dos valores de propriedades de dependência de leitura e gravação. O sistema de propriedades do WPF fornece mais segurança às propriedades de dependência somente leitura, assim você pode restringir o acesso de gravação.

Acesso e segurança de envoltórios 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 a definição de valores de propriedade. Se estiver incluído, o wrapper de propriedade CLR é um método de conveniência que implementa as chamadas estáticas GetValue e SetValue, que interagem com a propriedade de dependência subjacente. Essencialmente, um envoltório de propriedade CLR expõe uma propriedade de dependência como uma propriedade CLR suportada por uma propriedade de dependência, em vez de uma variável privada.

Aplicar mecanismos de segurança e restringir o acesso ao wrapper de propriedade CLR pode 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 é 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 fiquem cientes do verdadeiro nível de acesso da propriedade de dependência.

Exposição do sistema de propriedades para 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 é utilizável em chamadas GetValue e SetValue. 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 do evento que indicarão o DependencyProperty identificador para propriedades de dependência cujo valor foi alterado. Para informar 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 identificador de propriedade de dependência reduza private o número de maneiras pelas quais uma propriedade de dependência de leitura/gravação está 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 um ValidateValueCallback e esperar que a validação falhe devido à falha em Demand não é um mecanismo de segurança adequado para restringir alterações nos valores das propriedades. Além disso, a nova invalidação de valor imposta através de ValidateValueCallback pode ser suprimida por chamadores mal-intencionados, caso esses chamadores estejam operando no domínio de aplicação.

Acesso a propriedades de dependência somente para leitura

Para restringir o acesso, registre sua propriedade como uma propriedade de dependência de leitura única chamando o método RegisterReadOnly. O método RegisterReadOnly 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á apenas acesso de escrita àqueles que têm uma referência ao DependencyPropertyKey. Para ilustrar esse comportamento, o seguinte código de teste:

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

Consulte também