Почему некоторые свойства (например, IsSome и IsNone) для FSharpOption не видны с С#?

Мне кажется, что некоторые свойства типа опции F # не видны из проектов С#. Проверяя типы, я могу видеть более или менее причину, но я не совсем понимаю, что именно происходит, почему эти выборы были сделаны или как лучше всего обойти проблему.

Вот некоторые фрагменты, демонстрирующие проблему. У меня есть решение VS2015, содержащее два проекта, проект С# и проект F #. В проекте F # у меня есть класс, определяемый следующим образом:

type Foo () =

    member this.Bar () = Some(1)

Кроме того, в F # я могу написать что-то вроде этого:

let option = (new Foo()).Bar()
let result = if option.IsNone then "Is none" else "Is some"

Итак, похоже, что тип опции имеет свойство с именем IsNone. Теперь, в проекте С#, у меня есть ссылка на .dll, скомпилированный из проекта F #. Это позволяет мне писать, например,

var optionType = new Foo().Bar();

Переменная optionType - это FSharpOption<int>. Как я уже отмечал выше, когда я использую типы опций в проектах F #, я обычно имею доступ, например, к свойствам IsSome и IsNone. Однако, когда я пытаюсь написать что-то вроде optionType.IsNone, я получаю ошибку CS1546 "Свойство, индекс или событие... не поддерживается языком". В соответствии с этим Intellisense не обнаруживает свойство:

Intellisense не обнаруживает свойства IsSome и IsNone

Теперь, когда вы проверяете тип FSharpOption, я вижу, что свойства IsNone и IsSome отображаются как статические методы:

Подпись класса FSharpOption от С#

С другой стороны, когда я проверяю тип из F #, вместо этого я вижу следующее:

Подпись класса FSharpOption от F #

Здесь "существование" свойств IsSome и IsNone очевидно. Наводя курсор на эти свойства, VS2015 дает мне следующее примечание: "Содержащий тип может использовать" null "в качестве значения представления для своего случая с унифицированным объединением. Этот член будет скомпилирован как статический член". Именно по этой причине свойства недоступны, кроме как статические методы (как отмечают Лукев и Федор Сойкин).

Итак, ситуация выглядит так: Скомпилированный тип FSharpOption не имеет никаких свойств IsNone и IsSome. Что-то происходит за кулисами в F #, чтобы активировать эмуляцию этих свойств.

Я знаю, что обойти это можно, используя OptionModule в Microsoft.FSharp.Core. Однако, похоже, что эта функциональность является сознательным выбором для архитекторов основной библиотеки F #. Каковы причины выбора? И использует OptionModule правильное решение, или есть лучший способ использовать тип FSharpOption<T> из С#?

Ответ 1

Это связано с тем, как скомпилирован option. Значения Some скомпилируются непосредственно, создавая экземпляр класса и обертывая в нем значение. Но значения None не являются действительно значениями, они просто null.

Попробуйте следующее:

let a: int option = Some 1
let b: int option = None
let a_isNull = obj.ReferenceEquals( a, null )   // a_isNull = false
let b_isNull = obj.ReferenceEquals( b, null )   // b_isNull = true

(это также является причиной того, что значения None будут отображаться как null в окне просмотра отладчика)

Это оптимизация, которая экономит много циклов во время выполнения. (и вы можете использовать его для своих собственных типов соединений, применяя CompilationRepresentationFlags.UseNullAsTrueValue)

Теперь, поскольку некоторые значения этого типа могут быть null, вы не можете использовать свойства или методы для этих значений. Если значение будет null, вы просто потерпите крах. Вот почему вы всегда должны использовать OptionModule для всех операций.

Что касается того, почему эти свойства не отображаются в intellisense, это потому, что они static. Хотя я не уверен, почему они присутствуют там вообще. Возможно, артефакт компилятора.

Ответ 2

Я не очень хорошо разбираюсь в F #, но поскольку это все CLR, мой ответ с точки зрения С#:

В определении сгенерированного класса, как IsNone, так и IsSome являются статическими, и поэтому они не могут быть доступны через экземпляр optionType (ни через IntelliSense, ни в коде). Свойство Value не является статическим и может быть доступно.