Sichere Konstruktormuster für DependencyObjectsSafe Constructor Patterns for DependencyObjects

Im Allgemeinen sollten Klassenkonstruktoren Rückrufe wie virtuelle Methoden oder Delegaten nicht aufrufen, da Konstruktoren als Basisinitialisierung von Konstruktoren für eine abgeleitete Klasse aufgerufen werden können.Generally, class constructors should not call callbacks such as virtual methods or delegates, because constructors can be called as base initialization of constructors for a derived class. Eintritt in das Virtuelle kann möglicherweise bei einem unvollständigen Initialisierungszustand eines Objekts erfolgen.Entering the virtual might be done at an incomplete initialization state of any given object. Allerdings ruft das Eigenschaftensystem Rückrufe intern als Teil des Abhängigkeitseigenschaftensystem selbst auf und macht diese verfügbar.However, the property system itself calls and exposes callbacks internally, as part of the dependency property system. So einfach Vorgang der Einstellung eines Abhängigkeitseigenschaftswerts mit SetValue Aufruf möglicherweise umfasst einen Rückruf an einer beliebigen Stelle in der Bestimmung.As simple an operation as setting a dependency property value with SetValue call potentially includes a callback somewhere in the determination. Aus diesem Grund sollten Sie beim Einstellen der Eigenschaftswerte innerhalb des Texts eines Konstruktors vorsichtig sein, da dies problematisch werden kann, wenn Ihr Typ als Basisklasse verwendet wird.For this reason, you should be careful when setting dependency property values within the body of a constructor, which can become problematic if your type is used as a base class. Es gibt ein bestimmtes Muster zum Implementieren von DependencyObject Konstruktoren, die bestimmte Probleme mit abhängigkeitseigenschaftszuständen und den inhärenten Rückrufen, vermieden werden können, die hier dokumentiert ist.There is a particular pattern for implementing DependencyObject constructors that avoids specific problems with dependency property states and the inherent callbacks, which is documented here.

Eigenschaftensystem – Virtuelle MethodenProperty System Virtual Methods

Die folgenden virtuellen Methoden oder Rückrufe werden möglicherweise bei der Berechnung des Namens der SetValue Aufruf, der einen Abhängigkeitseigenschaftenwert festlegt: ValidateValueCallback, PropertyChangedCallback, CoerceValueCallback, OnPropertyChanged.The following virtual methods or callbacks are potentially called during the computations of the SetValue call that sets a dependency property value: ValidateValueCallback, PropertyChangedCallback, CoerceValueCallback, OnPropertyChanged. Diese virtuellen Methoden oder Rückrufe haben jeweils einen bestimmten Zweck bei der Erweiterung der Vielseitigkeit des Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF)-Eigenschaftensystems und der Abhängigkeitseigenschaften.Each of these virtual methods or callbacks serves a particular purpose in expanding the versatility of the Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) property system and dependency properties. Weitere Informationen zur Verwendung dieser virtuellen Methoden, um Eigenschaftswertbestimmungen anzupassen, finden Sie unter Rückrufe und Validierung von Abhängigkeitseigenschaften.For more information on how to use these virtuals to customize property value determination, see Dependency Property Callbacks and Validation.

Erzwingung der FXCop-Regel vs. Virtuelle Methoden des EigenschaftensystemsFXCop Rule Enforcement vs. Property System Virtuals

Wenn Sie das Microsoft-Tool FXCop als Teil des Buildprozesses verwenden und Sie entweder von bestimmten WPFWPF-Frameworkklassen ableiten, die den Basiskonstruktor aufrufen oder Ihre eigenen Abhängigkeitseigenschaften in abgeleiteten Klassen implementieren, treten möglicherweise bestimmte FXCop-Regelverstöße auf.If you use the Microsoft tool FXCop as part of your build process, and you either derive from certain WPFWPF framework classes calling the base constructor, or implement your own dependency properties on derived classes, you might encounter a particular FXCop rule violation. Der Name für diesen Verstoß ist:The name string for this violation is:

DoNotCallOverridableMethodsInConstructors

Dies ist eine Regel, die Teil der öffentlich festgelegten Standardregel für FXCop ist.This is a rule that is part of the default public rule set for FXCop. Was diese Regel möglicherweise meldet, ist eine Verfolgung durch das Abhängigkeitseigenschaftensystem, das schließlich ein Abhängigkeitseigenschaftensystem der virtuellen Methode aufruft.What this rule might be reporting is a trace through the dependency property system that eventually calls a dependency property system virtual method. Dieser Regelverstoß wird möglicherweise auch nach dem Befolgen der empfohlenen Konstruktormuster weiterhin angezeigt, die in diesem Thema dokumentiert werden, deshalb könnte es sein, dass Sie diese Regel in der FXCop-Regelsatzkonfiguration deaktivieren oder unterdrücken müssen.This rule violation might continue to appear even after following the recommended constructor patterns documented in this topic, so you might need to disable or suppress that rule in your FXCop rule set configuration.

Die meisten Probleme, die von abgeleiteten Klassen stammen, die keine vorhandenen Klassen verwendenMost Issues Come From Deriving Classes, Not Using Existing Classes

Die von dieser Regel gemeldeten Probleme treten auf, wenn eine Klasse, die Sie mit virtuellen Methoden in der Konstruktionsreihenfolge implementieren, anschließend abgeleitet wird.The issues reported by this rule occur when a class that you implement with virtual methods in its construction sequence is then derived from. Wenn Sie die Klasse versiegeln oder sonst erkennen oder erzwingen, dass die Klasse nicht abgeleitet wird, werden die hier erläuterten Aspekte und die Probleme, die die FXCop-Regel begründen, nicht für Sie gelten.If you seal your class, or otherwise know or enforce that your class will not be derived from, the considerations explained here and the issues that motivated the FXCop rule do not apply to you. Wenn Sie Klassen jedoch so entwickeln, dass sie dazu bestimmt sind, als Basisklassen verwendet zu werden, z.B. wenn Sie Vorlagen oder einen erweiterbaren Steuerelementbibliotheksatz erstellen, sollten Sie die hier empfohlenen Muster für Konstruktoren befolgen.However, if you are authoring classes in such a way that they are intended to be used as base classes, for instance if you are creating templates, or an expandable control library set, you should follow the patterns recommended here for constructors.

Standardkonstruktoren, die alle von Rückrufen angeforderten Werte initialisieren müssenDefault Constructors Must Initialize All Values Requested By Callbacks

Alle Instanzmember, die von Ihrer Klasse überschrieben werden oder von Rückrufen (die Rückrufe aus der Liste im Abschnitt Eigenschaftensystemmethoden) verwendet werden, müssen in Ihrem Standardkonstruktor der Klasse initialisiert werden, auch wenn einige dieser Werte durch „echte“ Werte über Parameter, die nicht standardmäßige Konstruktoren sind, ausgefüllt werden.Any instance members that are used by your class overrides or callbacks (the callbacks from the list in the Property System Virtuals section) must be initialized in your class default constructor, even if some of those values are filled by "real" values through parameters of the nondefault constructors.

Der folgende Beispielcode (und die nachfolgenden Beispiele), ist ein Pseudo-C#-Beispiel, das gegen diese Regel verstößt und das Problem erläutert:The following example code (and subsequent examples) is a pseudo-C# example that violates this rule and explains the problem:

public class MyClass : DependencyObject  
{  
    public MyClass() {}  
    public MyClass(object toSetWobble)  
        : this()  
    {  
        Wobble = toSetWobble; //this is backed by a DependencyProperty  
        _myList = new ArrayList();    // this line should be in the default ctor  
    }  
    public static readonly DependencyProperty WobbleProperty =   
        DependencyProperty.Register("Wobble", typeof(object), typeof(MyClass));  
    public object Wobble  
    {  
        get { return GetValue(WobbleProperty); }  
        set { SetValue(WobbleProperty, value); }  
    }  
    protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)  
    {  
        int count = _myList.Count;    // null-reference exception  
    }  
    private ArrayList _myList;  
}  

Wenn der Anwendungscode new MyClass(objectvalue) aufruft, ruft dies den Standardkonstruktor und die Basisklassenkonstruktoren auf.When application code calls new MyClass(objectvalue), this calls the default constructor and base class constructors. Dann legt Property1 = object1, ruft die virtuelle Methode OnPropertyChanged für die besitzende MyClass DependencyObject.Then it sets Property1 = object1, which calls the virtual method OnPropertyChanged on the owning MyClass DependencyObject. Die Überschreibung verweist auf _myList, was noch nicht initialisiert wurde.The override refers to _myList, which has not been initialized yet.

Eine Möglichkeit zur Vermeidung dieser Probleme, ist es sicherzustellen, dass Rückrufe nur andere Abhängigkeitseigenschaften verwenden, und dass jede dieser Abhängigkeitseigenschaften über einen festgelegten Standardwert als Teil ihrer registrierten Metadaten verfügt.One way to avoid these issues is to make sure that callbacks use only other dependency properties, and that each such dependency property has an established default value as part of its registered metadata.

Sichere KonstruktormusterSafe Constructor Patterns

Führen Sie diese Muster aus, um das Risiko einer unvollständigen Initialisierung zu vermeiden, wenn die Klasse als Basisklasse verwendet wird:To avoid the risks of incomplete initialization if your class is used as a base class, follow these patterns:

Standardkonstruktoren rufen Basisinitialisierung aufDefault constructors calling base initialization

Implementieren Sie diese Konstruktoren, in dem Sie den Basisstandardwert aufrufen:Implement these constructors calling the base default:

public MyClass : SomeBaseClass {  
    public MyClass() : base() {  
        // ALL class initialization, including initial defaults for   
        // possible values that other ctors specify or that callbacks need.  
    }  
}  

Nicht-standardmäßige (Komfort) Konstruktoren, die nicht den Basissignaturen entsprechenNon-default (convenience) constructors, not matching any base signatures

Wenn diese Konstruktoren Parameter verwenden, um Abhängigkeitseigenschaften in der Initialisierung festzulegen, rufen Sie zuerst Ihren eigenen Standardkonstruktor der Klasse für die Initialisierung auf, und verwenden Sie anschließend die Parameter zum Festlegen von Abhängigkeitseigenschaften.If these constructors use the parameters to set dependency properties in the initialization, first call your own class default constructor for initialization, and then use the parameters to set dependency properties. Dies können entweder von Ihrer Klasse definierte Abhängigkeitseigenschaften oder Abhängigkeitseigenschaften von Klassen sein, die von Basisklassen abstammen, verwenden Sie jedoch in beiden Fällen das folgende Muster:These could either be dependency properties defined by your class, or dependency properties inherited from base classes, but in either case use the following pattern:

public MyClass : SomeBaseClass {  
    public MyClass(object toSetProperty1) : this() {  
        // Class initialization NOT done by default.  
        // Then, set properties to values as passed in ctor parameters.  
        Property1 = toSetProperty1;  
    }  
}  

Nicht-standardmäßige (Komfort) Konstruktoren, die mit den Basissignaturen übereinstimmenNon-default (convenience) constructors, which do match base signatures

Anstatt den Basiskonstruktor mit der gleichen Parametrisierung aufzurufen, rufen Sie erneut den Standardkonstruktor Ihrer eigenen Klasse auf.Instead of calling the base constructor with the same parameterization, again call your own class' default constructor. Rufen Sie die Basisinitialisierung nicht auf. Rufen Sie stattdessen this() auf.Do not call the base initializer; instead you should call this(). Reproduzieren Sie anschließend das Verhalten des ursprünglichen Konstruktors, indem Sie die übergebenen Parameter als Werte zum Festlegen der entsprechenden Eigenschaften verwenden.Then reproduce the original constructor behavior by using the passed parameters as values for setting the relevant properties. Verwenden Sie die Dokumentation zur ursprünglichen Basisklasse als Hilfestellung bei der Ermittlung der Eigenschaften, die die einzelnen Parameter festlegen sollen:Use the original base constructor documentation for guidance in determining the properties that the particular parameters are intended to set:

public MyClass : SomeBaseClass {  
    public MyClass(object toSetProperty1) : this() {  
        // Class initialization NOT done by default.  
        // Then, set properties to values as passed in ctor parameters.  
        Property1 = toSetProperty1;  
    }  
}  

Alle Signaturen müssen übereinstimmenMust match all signatures

Für Fälle, in denen der Basistyp über mehrere Signaturen verfügt, müssen Sie absichtlich alle möglichen Signaturen Ihrer eigenen Konstruktorimplementierung zuordnen, die das empfohlene Muster vom Aufrufen der Standardkonstruktoren der Klasse verwenden, bevor Sie weitere Eigenschaften festlegen.For cases where the base type has multiple signatures, you must deliberately match all possible signatures with a constructor implementation of your own that uses the recommended pattern of calling the class default constructor before setting further properties.

Festlegen von Abhängigkeitseigenschaften mit SetValueSetting dependency properties with SetValue

Diese gleichen Muster gelten, wenn Sie eine Eigenschaft, die nicht über einen Wrapper zum einfacheren Festlegen von Eigenschaften festlegen, und legen Sie Werte mit SetValue.These same patterns apply if you are setting a property that does not have a wrapper for property setting convenience, and set values with SetValue. Ihre Aufrufe von SetValue , Pass-through-Konstruktorparameter sollten auch den Standardkonstruktor der Klasse für die Initialisierung aufrufen.Your calls to SetValue that pass through constructor parameters should also call the class' default constructor for initialization.

Siehe auchSee also