Säkerhet för beroendeegenskap (WPF .NET)
Tillgängligheten för läs-skriv-beroendeegenskaper genom Windows Presentation Foundation (WPF)-egenskapssystemet gör dem effektivt till publika egenskaper. Därför går det inte att skapa säkerhetsgarantier för värden för läs-och-skrivberoendeegenskaper. WPF-egenskapssystemet ger mer säkerhet för skrivskyddade beroendeegenskaper så att du kan begränsa skrivåtkomsten.
Åtkomst och säkerhet för egenskapsomslutningar
En CLR-egenskapsomslutning (Common Language Runtime) ingår vanligtvis i implementeringar av läs- och skrivberoendeegenskaper för att förenkla att hämta eller ange egenskapsvärden. Om den ingår är CLR-egenskapsomslutaren en hjälpmetod som implementerar de statiska anropen GetValue och SetValue, vilka interagerar med den underliggande beroendeegenskapen. I princip exponerar en CLR-egenskapsomslutning en beroendeegenskap som en CLR-egenskap som backas upp av en beroendeegenskap snarare än ett privat fält.
Att tillämpa säkerhetsmekanismer och begränsa åtkomsten till CLR-egenskapsomslutningen kan förhindra användning av bekvämlighetsmetoden, men dessa tekniker förhindrar inte direkta anrop till GetValue
eller SetValue
. Med andra ord är en läs- och skrivbar beroendeegenskap alltid tillgänglig via WPF-egenskapssystemet. Om du implementerar en läs-skriv beroendeegenskap bör du undvika att begränsa åtkomsten till CLR-egenskapsomslutningen. Deklarera i stället CLR-egenskapsomslutningen som en offentlig medlem så att anropare är medvetna om den verkliga åtkomstnivån för beroendeegenskapen.
Exponering av beroendeegenskaper i fastighetssystemet
WPF-egenskapssystemet ger åtkomst till en skriv-och-läs beroendeegenskap via dess DependencyProperty identifierare. Identifieraren kan användas i GetValue- och SetValue-anrop. Även om fältet för statisk identifierare inte är offentligt returnerar flera aspekter av egenskapssystemet en DependencyProperty
eftersom den finns på en instans av en klass eller härledd klass. Metoden GetLocalValueEnumerator returnerar till exempel identifierare för beroendeegenskapsinstanser med ett lokalt angivet värde. Du kan också åsidosätta den OnPropertyChanged virtuella metoden för att ta emot händelsedata som rapporterar DependencyProperty
identifierare för beroendeegenskaper som har ändrat värde. För att göra anropare medvetna om den sanna åtkomstnivån för en läs- och skrivbar beroendeegenskap, deklarerar du dess identifierarfält som en offentlig medlem.
Not
Även om du deklarerar ett beroendeegenskapsfält som private
minskar antalet sätt som en läs- och skrivbar beroendeegenskap är tillgänglig på, kommer egenskapen enligt CLR-språkets definition inte att vara privat.
Valideringssäkerhet
Att tillämpa en Demand på en ValidateValueCallback och förvänta sig att verifieringen misslyckas om Demand
inte fungerar är inte en adekvat säkerhetsmekanism för att begränsa ändringar av egenskaper. Dessutom kan ogiltighet av nytt värde som framtvingas via ValidateValueCallback
ignoreras av skadliga anropare, om dessa anropare arbetar inom programdomänen.
Åtkomst till skrivskyddade beroendeegenskaper
Om du vill begränsa åtkomsten registrerar du din egenskap som en skrivskyddad beroendeegenskap genom att anropa metoden RegisterReadOnly. Metoden RegisterReadOnly
returnerar en DependencyPropertyKey, som du kan tilldela till ett icke-offentligt klassfält. För skrivskyddade beroendeegenskaper ger WPF-egenskapssystemet endast skrivåtkomst till dem som har en referens till DependencyPropertyKey
. För att illustrera det här beteendet, följande testkod:
- Instansierar en klass som implementerar både läs- och skrivbara och skrivskyddade beroendeegenskaper.
- Tilldelar en
private
åtkomstmodifierare till varje identifierare. - Implementerar endast
get
-åtkomster. - Använder metoden GetLocalValueEnumerator för att komma åt de underliggande beroendeegenskaperna via WPF-egenskapssystemet.
- Anropar GetValue och SetValue för att testa åtkomsten till varje beroendeegenskapsvärde.
/// <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
Se även
.NET Desktop feedback