Condividi tramite


Sicurezza delle proprietà di dipendenza (WPF .NET)

L'accessibilità delle proprietà di dipendenza leggibili e scrivibili tramite il sistema di proprietà Windows Presentation Foundation (WPF) li rende effettivamente proprietà pubbliche. Di conseguenza, non è possibile garantire la sicurezza sui valori delle proprietà di dipendenza lettura-scrittura. Il sistema di proprietà WPF offre maggiore sicurezza per le proprietà di dipendenza di sola lettura, in modo da limitare l'accesso in scrittura.

Accesso e sicurezza degli involucro delle proprietà

Un wrapper di proprietà del Common Language Runtime (CLR) è solitamente incluso nelle implementazioni delle proprietà di dipendenza leggibili e scrivibili per semplificare l'accesso o l'impostazione dei valori delle proprietà. Se incluso, il wrapper della proprietà CLR è un metodo conveniente che implementa le chiamate statiche GetValue e SetValue che interagiscono con la proprietà di dipendenza sottostante. Essenzialmente, un wrapper di proprietà CLR espone una proprietà di dipendenza come se fosse una proprietà CLR che si appoggia a una proprietà di dipendenza anziché a un campo privato.

L'applicazione di meccanismi di sicurezza e la limitazione dell'accesso al wrapper delle proprietà CLR potrebbero impedire l'utilizzo del metodo pratico, ma queste tecniche non impediscono chiamate dirette a GetValue o SetValue. In altre parole, una proprietà dipendente di lettura/scrittura è sempre accessibile attraverso il sistema di proprietà WPF. Quando implementate una proprietà di dipendenza di lettura/scrittura, evitate di limitare l'accesso al wrapper della proprietà CLR. Dichiarare invece il wrapper della proprietà CLR come membro pubblico in modo che i chiamanti possano essere consapevoli del vero livello di accesso della proprietà di dipendenza.

Visibilità delle proprietà di dipendenza nel sistema di proprietà

Il sistema di proprietà WPF fornisce l'accesso a una proprietà dipendente a lettura e scrittura tramite il relativo identificatore DependencyProperty. L'identificatore è utilizzabile nelle chiamate GetValue e SetValue. Anche se il campo identificatore statico non è pubblico, diversi aspetti del sistema di proprietà restituiranno un DependencyProperty come esiste in un'istanza di una classe o di una classe derivata. Ad esempio, il metodo GetLocalValueEnumerator restituisce gli identificatori per le istanze di proprietà di dipendenza con un valore impostato localmente. È anche possibile eseguire l'override del metodo virtuale OnPropertyChanged per ricevere i dati dell'evento che riporteranno l'identificatore DependencyProperty per le proprietà di dipendenza che hanno cambiato valore. Per rendere i chiamanti consapevoli del livello di accesso effettivo di una proprietà di dipendenza di lettura/scrittura, dichiarare il relativo campo identificatore come membro pubblico.

Nota

Sebbene la dichiarazione di un campo dell'identificatore di proprietà di dipendenza come private riduce il numero di modi in cui una proprietà di dipendenza di lettura/scrittura è accessibile, la proprietà non verrà privata in base alla definizione del linguaggio CLR.

Sicurezza della convalida

L'applicazione di un Demand a un ValidateValueCallback e l'attesa che la convalida fallisca al verificarsi del Demand, non è un meccanismo di sicurezza adeguato per limitare le modifiche ai valori delle proprietà. Inoltre, la nuova invalidazione del valore applicata tramite ValidateValueCallback può essere eliminata da chiamanti malintenzionati, se tali chiamanti operano all'interno del dominio applicazione.

Accesso alle proprietà di dipendenza di sola lettura

Per limitare l'accesso, registrare la proprietà come proprietà di dipendenza di sola lettura chiamando il metodo RegisterReadOnly. Il metodo RegisterReadOnly restituisce un DependencyPropertyKey, che è possibile assegnare a un campo di classe non pubblico. Per le proprietà di dipendenza di sola lettura, il sistema di proprietà WPF fornirà solo l'accesso in scrittura a coloro che dispongono di un riferimento al DependencyPropertyKey. Per illustrare questo comportamento, il codice di test seguente:

  • Crea un'istanza di una classe che implementa proprietà di dipendenza sia di lettura e scrittura sia di sola lettura.
  • Assegna un modificatore di accesso private a ogni identificatore.
  • Implementa solo le funzioni di accesso get.
  • Usa il metodo GetLocalValueEnumerator per accedere alle proprietà di dipendenza sottostanti tramite il sistema di proprietà WPF.
  • Chiama GetValue e SetValue per testare l'accesso a ciascun valore delle proprietà di dipendenza.
    /// <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

Vedere anche