Sicurezza delle proprietà di dipendenza (WPF .NET)
L'accessibilità delle proprietà di dipendenza di lettura-scrittura 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 di lettura-scrittura. Il sistema di proprietà WPF offre maggiore sicurezza per le proprietà di dipendenza di sola lettura in modo che sia possibile limitare l'accesso in scrittura.
Importante
La documentazione di Guida desktop per .NET 7 e .NET 6 è in fase di costruzione.
Accesso e sicurezza dei wrapper delle proprietà
Un wrapper di proprietà CLR (Common Language Runtime) è in genere incluso nelle implementazioni delle proprietà di dipendenza di lettura-scrittura per semplificare il recupero o l'impostazione dei valori delle proprietà. Se incluso, il wrapper della proprietà CLR è un metodo pratico che implementa le GetValue chiamate statiche e SetValue che interagiscono con la proprietà di dipendenza sottostante. Essenzialmente, un wrapper di proprietà CLR espone una proprietà di dipendenza come proprietà CLR supportata da una proprietà di dipendenza anziché da 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 impediranno chiamate dirette a GetValue
o SetValue
. In altre parole, una proprietà di dipendenza di lettura-scrittura è sempre accessibile tramite il sistema di proprietà WPF. Se si implementa una proprietà di dipendenza di lettura-scrittura, evitare di limitare l'accesso al wrapper delle proprietà CLR. Dichiarare invece il wrapper della proprietà CLR come membro pubblico in modo che i chiamanti siano consapevoli del vero livello di accesso della proprietà di dipendenza.
Esposizione del sistema di proprietà delle proprietà di dipendenza
Il sistema di proprietà WPF fornisce l'accesso a una proprietà di dipendenza di lettura-scrittura tramite il relativo DependencyProperty identificatore. L'identificatore è utilizzabile in GetValue e SetValue chiamate. Anche se il campo identificatore statico non è pubblico, diversi aspetti del sistema di proprietà restituiranno un oggetto DependencyProperty
come esiste in un'istanza di una classe o di una classe derivata. Ad esempio, il GetLocalValueEnumerator metodo restituisce gli identificatori per le istanze delle proprietà di dipendenza con un valore impostato in locale. È anche possibile eseguire l'override del OnPropertyChanged metodo virtuale per ricevere i dati degli eventi che segnalano l'identificatore per le proprietà di dipendenza che hanno modificato il DependencyProperty
valore. Per rendere i chiamanti consapevoli del vero livello di accesso di una proprietà di dipendenza di lettura-scrittura, dichiarare il campo identificatore come membro pubblico.
Nota
Anche se dichiara un campo identificatore di proprietà di dipendenza come private
riduce il numero di modi in cui è accessibile una proprietà di dipendenza di lettura-scrittura, la proprietà non sarà privata in base alla definizione del linguaggio CLR.
Sicurezza di convalida
L'applicazione di un oggetto Demand a un ValidateValueCallback oggetto e prevede che la convalida non riesca Demand
in caso di errore, non è un meccanismo di sicurezza adeguato per limitare le modifiche al valore della proprietà. Inoltre, la nuova invalidazione del valore applicata tramite ValidateValueCallback
può essere eliminata dai chiamanti malintenzionati, se tali chiamanti operano all'interno del dominio dell'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 RegisterReadOnly metodo . Il RegisterReadOnly
metodo restituisce un DependencyPropertyKeyoggetto , 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 hanno un riferimento a DependencyPropertyKey
. Per illustrare questo comportamento, il codice di test seguente:
- Crea un'istanza di una classe che implementa le proprietà di dipendenza di sola lettura e di sola lettura.
- Assegna un
private
modificatore di accesso a ogni identificatore. - Implementa solo le funzioni di
get
accesso. - Usa il GetLocalValueEnumerator metodo per accedere alle proprietà di dipendenza sottostanti tramite il sistema di proprietà WPF.
- Chiamate GetValue e SetValue per testare l'accesso a ogni valore della 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