DependencyObject の安全なコンストラクター パターンSafe Constructor Patterns for DependencyObjects

一般的に、コンストラクターは派生クラスのコンストラクターの基底の初期化として呼び出されることがあるため、クラスのコンストラクターでは、仮想メソッドやデリゲートなどのコールバックを呼び出しません。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. 対象オブジェクトの初期化が不完全な状態で、仮想メソッドに入ることがあります。Entering the virtual might be done at an incomplete initialization state of any given object. ただし、プロパティ システム自体は、依存関係プロパティ システムの一部としてコールバックを呼び出し、内部的に公開します。However, the property system itself calls and exposes callbacks internally, as part of the dependency property system. SetValue の呼び出しで依存関係プロパティの値を設定する操作と同様に、特定の場所にコールバックが含まれる可能性があります。As simple an operation as setting a dependency property value with SetValue call potentially includes a callback somewhere in the determination. このため、使用する型が基底クラスとして使われる場合に、コンストラクター本体内に依存関係プロパティ値を設定すると問題が発生する可能性があり、注意が必要です。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. 依存関係プロパティの状態と固有のコールバックに関する特定の問題を回避する DependencyObject コンストラクターを実装するための特定のパターンがあります。これについては、こちらを参照してください。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.

プロパティ システムの仮想メソッドProperty System Virtual Methods

依存関係プロパティ値 (ValidateValueCallbackPropertyChangedCallbackCoerceValueCallbackOnPropertyChanged) を設定する SetValue 呼び出しの計算中に、次の仮想メソッドまたはコールバックが呼び出される可能性があります。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. これらの仮想メソッドまたはコールバックは、Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) のプロパティ システムと依存関係プロパティの汎用性を高めるうえで、それぞれ特定の目的を果たします。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. これらの仮想メソッドを使用してプロパティ値の決定をカスタマイズする方法の詳細については、「依存関係プロパティのコールバックと検証」を参照してください。For more information on how to use these virtuals to customize property value determination, see Dependency Property Callbacks and Validation.

FXCop ルールの強制とプロパティシステム純粋FXCop Rule Enforcement vs. Property System Virtuals

ビルド プロセスの一部として Microsoft ツールの FXCop を使用している場合、基底コンストラクターを呼び出す特定の WPFWPF フレームワーク クラスを派生させるとき、派生クラスで独自の依存関係プロパティを実装するときに、FXCop の特定のルール違反が発生することがあります。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. ルール違反に該当する名前文字列は次のとおりです。The name string for this violation is:

DoNotCallOverridableMethodsInConstructors

このルールは、FXCop に設定されている既定のパブリック ルールの一部です。This is a rule that is part of the default public rule set for FXCop. このルールによって報告されるのは、依存関係プロパティ システムの仮想メソッドを最終的に呼び出す、依存関係プロパティ システム内のトレースです。What this rule might be reporting is a trace through the dependency property system that eventually calls a dependency property system virtual method. このルール違反は、このトピックで説明されている推奨コンストラクター パターンに従っている場合も発生し続ける可能性があるため、FXCop のルール セット構成でこのルールを無効にするか抑制する必要があります。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.

既存のクラスの使用ではなくクラスの派生が大半の原因Most Issues Come From Deriving Classes, Not Using Existing Classes

このルールによって報告される問題は、構築のシーケンスで仮想メソッドを呼び出すように実装したクラスが派生される場合に発生します。The issues reported by this rule occur when a class that you implement with virtual methods in its construction sequence is then derived from. クラスをシールする場合、クラスが派生されないとわかっている場合、クラスが派生されないように強制する場合は、ここで説明する内容や FXCop のルールによって起こる問題は該当しません。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. ただし、テンプレートや拡張可能なコントロール ライブラリのセットを作成する場合などのように、基底クラスとしての使用を想定したクラスを作成する場合は、ここで説明されているコンストラクターの推奨パターンに従う必要があります。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.

既定のコンストラクターにおいて、コールバックによって要求されるすべての値の初期化の必要性Default Constructors Must Initialize All Values Requested By Callbacks

クラスによって使用されるすべてのインスタンスメンバー (プロパティシステム純粋セクション内のリストからのコールバック) は、パラメーターなしのクラスコンストラクターで初期化する必要があります。これらの値の一部は、によって "real" 値によって入力されます。パラメーターなしのコンストラクターのパラメーター。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 parameterless constructor, even if some of those values are filled by "real" values through parameters of the nonparameterless constructors.

次のコード例 (および以降の例) は、この規則に違反する擬似 C# コードの例であり、問題を説明しています。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;  
}  

アプリケーションコードが new MyClass(objectvalue)を呼び出すと、パラメーターなしのコンストラクターと基底クラスのコンストラクターが呼び出されます。When application code calls new MyClass(objectvalue), this calls the parameterless constructor and base class constructors. 次に Property1 = object1を設定します。これにより、所有している MyClass DependencyObjectの仮想メソッド OnPropertyChanged が呼び出されます。Then it sets Property1 = object1, which calls the virtual method OnPropertyChanged on the owning MyClass DependencyObject. オーバーライドでは、まだ初期化されていない _myList が参照されます。The override refers to _myList, which has not been initialized yet.

これらの問題を回避する方法の 1 つは、コールバックが他の依存関係プロパティのみを使用し、それぞれの使用する依存関係プロパティが、登録済みのメタデータの一部として確立された既定値を持つようにすることです。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.

安全なコンストラクター パターンSafe Constructor Patterns

クラスが基底クラスとして使用される場合に、不完全な初期化のリスクを回避するには、次のパターンに従ってください。To avoid the risks of incomplete initialization if your class is used as a base class, follow these patterns:

パラメーターなしのコンストラクターによる基本初期化の呼び出しParameterless constructors calling base initialization

基底クラスの既定値を呼び出す次のコンストラクターを実装します。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.  
    }  
}  

基底クラスのシグネチャと一致しない、既定以外の (簡易) コンストラクターNon-default (convenience) constructors, not matching any base signatures

これらのコンストラクターがパラメーターを使用して初期化時に依存関係プロパティを設定する場合は、まず、初期化のために独自のクラスのパラメーターなしのコンストラクターを呼び出し、次にパラメーターを使用して依存関係プロパティを設定します。If these constructors use the parameters to set dependency properties in the initialization, first call your own class parameterless constructor for initialization, and then use the parameters to set dependency properties. これらは、クラスによって定義された依存関係プロパティか、基底クラスから継承された依存関係プロパティのいずれかですが、いずれの場合も次のパターンが適用されます。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;  
    }  
}  

基底クラスのシグネチャと一致する、既定以外の (簡易) コンストラクターNon-default (convenience) constructors, which do match base signatures

同じパラメーター化を使用して基底コンストラクターを呼び出す代わりに、独自のクラスのパラメーターなしのコンストラクターを再度呼び出します。Instead of calling the base constructor with the same parameterization, again call your own class' parameterless constructor. 基底初期化子を呼び出さないでください。代わりに this() を呼び出す必要があります。Do not call the base initializer; instead you should call this(). 次に、渡されたパラメーターを関連プロパティを設定する値として使用し、元のコンストラクターの動作を複製します。Then reproduce the original constructor behavior by using the passed parameters as values for setting the relevant properties. 特定のパラメーターを設定するプロパティを決定する場合は、参考として元の基底コンストラクターのドキュメントを使用します。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;  
    }  
}  

すべてのシグネチャを一致させることが必要Must match all signatures

基本型に複数のシグネチャがある場合は、さらに設定する前に、使用可能なすべてのシグネチャを独自のコンストラクター実装と意図的に一致させる必要があります。属性.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 parameterless constructor before setting further properties.

SetValue による依存関係プロパティの設定Setting dependency properties with SetValue

プロパティ設定の便宜のためにラッパーを持たないプロパティを設定し、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. コンストラクターパラメーターをパススルーする SetValue を呼び出すと、初期化のためにクラスのパラメーターなしのコンストラクターも呼び出される必要があります。Your calls to SetValue that pass through constructor parameters should also call the class' parameterless constructor for initialization.

参照See also