如何在构造函数被调用之前初始化F#中的字段?

问题描述:

此代码无法正常工作,因为GetEntriesChangedObservable由基础构造函数调用,FMyEntriesnull,因为它自己的构造函数尚未调用。如何在构造函数被调用之前初始化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 

应该如何FMyEntries进行初始化,使其具有任何构造函数之前的值被调用?

在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); 
    } 

编辑: 简短的回答,这是不可能的!即使应用Fyodor Soikin提出的破解,F#运行时也会检查初始化过程,并在构造函数尝试访问任何成员(如果它自己的实例)时引发异常。

我最终通过添加与构造函数相同的Initialize()函数来解决它。

+0

在找到'FMyEntries'为空的地方,调用堆栈是什么? –

+0

好点,是在错误的轨道上。我编辑了这个问题。 – thalm

通常情况下,在调用基构造函数之前,F#中不可能初始化字段/属性,因此您将无法创建这样的类(这里我必须注意:通常对于祖先构造函数来说,指望后代被初始化;我知道,小安慰)。

但有一个例外:在一个对象表达式中,封闭变量在基础构造函数被调用之前被初始化。所以,你可以应用此略有丑陋的解决方法:

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 } 

注意: 通常情况下,你可以直接创建基类(即{ new DynamicEnumDefinitionBase with ... }),但在这种情况下,你有一个派生类为供应泛型参数。另一种设计气味。

+0

这看起来很天才,但不幸的是在我的情况下不起作用,因为基类将是一个单例,并用子类的主构造函数构造单例实例,这就是为什么它需要子类作为泛型参数并具有new()约束它。你能想到一个带有GetMyEntries函数的解决方案,该函数检查FMyEntries的访问权限,如果它是空的,然后初始化它? – thalm

+0

我目前的做法是在这里:https://pastebin.com/yTXw1CXX唯一的问题是,这两个做语句不能像这样工作... – thalm

+0

Nah,没有工作,因为F#检查构造函数的成员访问和抛出一个“对象或值的初始化导致对象或值在其完全初始化之前递归访问”。异常...所以我会尝试进一步抽象类和主要构造函数 – thalm