Почему некоторые свойства (например, 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
не является статическим и может быть доступно.