2017-08-08 3 views
2

GetEntriesChangedObservableが基本コンストラクタから呼び出され、FMyEntriesnullであるため、このコンストラクタがまだ呼び出されていないため、このコードは正しく動作しません。いずれかのコンストラクタが呼び出される前に、それが価値を持つようにFMyEntries初期化する必要がありますどのようにコンストラクタが呼び出される前にF#のフィールドを初期化するには?

type MyEnumDefinition() = 
    inherit DynamicEnumDefinitionBase<MyEnumDefinition>() 

    let FMyEntries : ObservableCollection<string> = ObservableCollection<string>() 

    //gets called from base constructor 
    override this.GetEntriesChangedObservable() = 
     Observable.FromEventPattern<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>( 
      (fun h -> FMyEntries.CollectionChanged.AddHandler h), //FMyEntries is null 
      (fun h -> FMyEntries.CollectionChanged.RemoveHandler h)) //FMyEntries is null 

C#ではそれは次のようになります。

//initialized before constructor 
    ObservableCollection<string> FMyEntries = new ObservableCollection<string>(); 

    //gets called from base constructor 
    protected override IObservable<object> GetEntriesChangedObservable() 
    { 
     return Observable.FromEventPattern<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>(
      h => FMyEntries.CollectionChanged += h, 
      h => FMyEntries.CollectionChanged -= h); 
    } 

EDIT: 短い答えを、それは不可能です! Fyodor Soikinによって提案されたハックを適用しても、F#ランタイムは初期化プロセスをチェックし、コンストラクタが独自のインスタンスの場合に任意のメンバにアクセスしようとすると例外をスローします。

最後に、コンストラクタと同じようにInitialize()関数を追加して解決しました。

+0

'FMyEntries'がヌルであると判明した時点のコールスタックは何ですか? –

+0

良い点は、間違ったトラックにあった。私は質問を編集しました。 – thalm

答えて

2

一般に、F#では、ベースコンストラクタを呼び出す前にフィールド/プロパティを初期化することはできないため、そのようなクラスを作成することはできません(ここで私は注意しなければならない:一般的に祖先コンストラクタ子孫が初期化されているかどうかを調べる;私は小さい慰めを知っている)。

ただし、オブジェクト式では、基本コンストラクタが呼び出される前に閉じられた変数が初期化されます。供給するために、派生クラスを持つこと は通常、あなたが(すなわち{ new DynamicEnumDefinitionBase with ... })直接の基本クラスを作成することもできますが、この場合には、あなたを持っている:

type [<AbstractClass>] MyEnumDefinition() = 
    inherit DynamicEnumDefinitionBase<MyEnumDefinition>() 

    abstract member GetMyEntries: unit -> ObservableCollection<string> 

    override this.GetEntriesChangedObservable() = 
     Observable.FromEventPattern<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>( 
      (fun h -> this.GetMyEntries().CollectionChanged.AddHandler h), 
      (fun h -> this.GetMyEntries().CollectionChanged.RemoveHandler h))) 

let mkMyEnumDefinition() = 
    let MyEntries = ObservableCollection<string>() 
    { new MyEnumDefinition() with override __.GetMyEntries() = MyEntries } 

:だから、この少し醜い回避策を適用することができますジェネリックパラメータ。別のデザインの匂い。

+0

これは天才ですが、私の場合、ベースクラスはシングルトンになり、サブクラスのプライマリコンストラクタでシングルトンインスタンスを構築するため、残念ながら機能しません。そのため、サブクラスをジェネリックパラメータとして必要とし、new()それに対する制約。あなたは、アクセス時にFMyEntriesをチェックするGetMyEntries関数を持つソリューションを考えることができますか?それがヌルであり、それを初期化するのですか? – thalm

+0

私の現在のアプローチはここにあります:https://pastebin.com/yTXw1CXX唯一の問題は、2つのdo文がもうこのようには動作しないということです... – thalm

+0

Nahは、F#がコンストラクタからのメンバアクセスをチェックするように動作しませんでした。 「オブジェクトまたは値の初期化によって、オブジェクトまたは値が完全に初期化される前に再帰的にアクセスされる」という結果がスローされます。例外...私は抽象クラスとプライマリコンストラクタでさらに試してみます – thalm

関連する問題