Sicherheit von Abhängigkeitseigenschaften (WPF .NET)

Die Zugänglichkeit les- und schreibbarer Abhängigkeitseigenschaften über das Windows Presentation Foundation (WPF)-Eigenschaftssystem macht diese praktisch zu öffentlichen Eigenschaften. Daher gibt es keine Sicherheitsgarantien bezüglich les- und schreibbarer Abhängigkeitseigenschaften. Das WPF-Eigenschaftssystem bietet mehr Sicherheit für schreibgeschützte Abhängigkeitseigenschaften, sodass Sie den Schreibzugriff einschränken können.

Wichtig

Der Desktopleitfaden zu .NET 7 und .NET 6 ist in Bearbeitung.

Zugriff auf und Sicherheit von Eigenschaftenwrappern

Implementierungen von Lese-Schreib-Abhängigkeitseigenschaften enthalten in der Regel einen Common Language Runtime(CLR)-Eigenschaftenwrapper für einfache Get- und Set-Zugriffe auf Eigenschaftswerte. Der CLR-Eigenschaftenwrapper ist eine Hilfsmethode, die die statischen Aufrufe GetValue und SetValue implementiert, die mit der zugrunde liegenden Abhängigkeitseigenschaft interagieren. Im Wesentlichen macht ein CLR-Eigenschaftenwrapper eine Abhängigkeitseigenschaft als CLR-Eigenschaft verfügbar, die aus einer Abhängigkeitseigenschaft anstelle eines privaten Felds besteht.

Das Anwenden von Sicherheitsmechanismen und das Einschränken des Zugriffs auf den CLR-Eigenschaftenwrapper kann die Verwendung der Hilfsmethode verhindern, nicht aber direkte Aufrufe von GetValue oder SetValue. Les- und schreibbare Abhängigkeitseigenschaften sind also immer über das WPF-Eigenschaftensystem zugänglich. Wenn Sie eine les- und schreibbare Abhängigkeitseigenschaft implementieren, vermeiden Sie es, den Zugriff auf den CLR-Eigenschaftenwrapper einzuschränken. Deklarieren Sie stattdessen den CLR-Eigenschaftenwrapper als öffentliches Member, sodass aufrufende Methoden die wahre Zugriffsebene der Abhängigkeitseigenschaft kennen.

Offenlegen des Eigenschaftensystems von Abhängigkeitseigenschaften

Das WPF-Eigenschaftssystem bietet Zugriff auf eine les- und schreibbare Abhängigkeitseigenschaft über den Bezeichner DependencyProperty. Der Bezeichner kann in GetValue- und SetValue-Aufrufen verwendet werden. Selbst wenn das statische Bezeichnerfeld nicht öffentlich ist, geben mehrere Aspekte des Eigenschaftssystems eine DependencyProperty so zurück, wie sie in einer Instanz einer Klasse oder abgeleiteten Klasse vorliegt. Beispielsweise gibt die GetLocalValueEnumerator-Methode Bezeichner für Abhängigkeitseigenschaft-Instanzen mit einem lokal festgelegten Wert zurück. Außerdem können Sie die virtuelle Methode OnPropertyChanged überschreiben, um Ereignisdaten zu erhalten, die den Bezeichner DependencyProperty für Abhängigkeitseigenschaften mit geändertem Wert melden, die geänderten Wert haben. Um aufrufende Methoden auf die wahre Zugriffsebene einer les- und schreibbare Abhängigkeitseigenschaft aufmerksam zu machen, deklarieren Sie das Bezeichnerfeld als öffentliches Member.

Hinweis

Obwohl das Deklarieren eines Abhängigkeitseigenschafts-Bezeichnerfelds als private die Anzahl der Zugriffsmöglichkeiten auf eine Lesezugriffseigenschaft verringert, wird die Eigenschaft nicht privat gemäß der CLR-Sprachdefinition sein.

Validierungssicherheit

Um das Ändern von Eigenschaftswerten restringieren, ist es nicht ausreichend, ein Demand auf ein ValidateValueCallback anzuwenden und zu erwarten, dass die Validierung aufgrund eines Demand-Fehlers fehlschlägt. Außerdem kann die von ValidateValueCallback erzwungene Invalidierung neuer Werte auch von böswilligen Aufrufern unterdrückt werden, wenn diese Aufrufer innerhalb der Anwendungsdomäne agieren.

Zugreifen auf schreibgeschützte Abhängigkeitseigenschaften

Um den Zugriff einzuschränken, registrieren Sie Ihre Eigenschaft als schreibgeschützte Abhängigkeitseigenschaft, indem Sie die Methode RegisterReadOnly aufrufen. Die Methode RegisterReadOnly gibt eine DependencyPropertyKey zurück, die Sie einem nicht öffentlichen Klassenfeld zuweisen können. Für schreibgeschützte Abhängigkeitseigenschaften bietet das WPF-Eigenschaftssystem nur Schreibzugriff für Entitäten, die über einen Verweis auf den DependencyPropertyKey verfügen. Um dieses Verhalten zu veranschaulichen, führen Sie den folgenden Testcode aus:

  • Instanziiert eine Klasse, die sowohl schreibbare als auch schreibgeschützte Abhängigkeitseigenschaften implementiert.
  • Weist jedem Bezeichner einen private-Zugriffsmodifizierer zu.
  • Implementiert nur get-Zugriffsmethoden.
  • Verwendet die Methode GetLocalValueEnumerator, um auf die zugrunde liegenden Abhängigkeitseigenschaften über das WPF-Eigenschaftssystem zuzugreifen.
  • Ruft GetValue und SetValue auf, um den Zugriff auf die jeweiligen Abhängigkeitseigenschaftswerte zu prüfen.
    /// <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

Weitere Informationen