Вы можете использовать поддержку F # для рекурсивной инициализации даже при создании экземпляра абстрактного класса:
let makeParent() =
let rec children = seq [ Child(parent); Child(parent); Child(parent) ]
and parent =
{ new Parent() with
member __.Children = children }
parent
При компиляции кода F # использует ленивые значения, поэтому значение children
становится ленивым значением исвойство Children
обращается к значению этого ленивого вычисления.Это нормально, потому что он может сначала создать экземпляр Parent
(ссылаясь на ленивое значение), а затем фактически создать последовательность.
Выполнение того же самого с записями не будет работать так же хорошо, потому что ни один извычисления могут быть отложены, но здесь это работает очень хорошо, потому что последовательность фактически не доступна при создании Parent
(если бы это была запись, это было бы поле, которое нужно было бы оценить).
Компилятор F # не может сказать (в общем), является ли это правильным, поэтому он выдает предупреждение, которое можно отключить с помощью #nowarn "40"
.
В общем, я думаю, что использование let rec .. and ..
для инициализации рекурсивных значенийхорошая вещь - она немного ограничена (одна из ссылок должна быть отложена), но она заставляет вас держать рекурсивные ссылки изолированными и, я думаю, делает ваш код проще.
РЕДАКТИРОВАТЬ Чтобы добавить пример, когда это может пойти не так, - если конструктор Child
пытается получить доступ к коллекции Children
своего родителя, он вызываетоценка ленивого значения до того, как оно может быть создано, и вы получите ошибку времени выполнения (о чем говорит предупреждение).Попробуйте добавить это в конструктор Child
:
do printfn "%d" (Seq.length parent.Children)