为什么从C#中看不到FSharpOption的某些属性(例如IsSome和IsNone)?

时间:2017-02-01 15:34:10

标签: c# properties f# interop

在我看来,F#选项类型的某些属性在C#项目中是不可见的。通过检查类型,我可以看到或多或少的原因,但我不能真正理解究竟发生了什么,为什么做出这些选择或如何最好地规避问题。

这里有一些片段展示了这个问题。我有一个包含两个项目的VS2015解决方案,一个C#项目和一个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的属性。现在,在C#项目中,我引用了从F#项目编译的.dll。这允许我写例如。

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

变量optionTypeFSharpOption<int>。如上所述,当我在F#项目中使用选项类型时,我通常可以访问IsSomeIsNone属性。但是,当我尝试写optionType.IsNone之类的东西时,我得到CS1546错误&#34;语言&#34;不支持属性,索引器或事件......与此一致,Intellisense没有检测到属性:

Intellisense does not detect the IsSome and IsNone properties

现在,在检查FSharpOption类型时,我可以看到IsNone和IsSome&#34;属性&#34;显示为静态方法:

FSharpOption class signature from C#

另一方面,当我从F#检查类型时,我看到以下内容:

FSharpOption class signature from F#

在这里,&#34;存在&#34;属性IsSomeIsNone很明显。将光标悬停在这些属性上,VS2015给出了以下注释:&#34;包含类型可以使用&#39; null&#39;作为其结合工会案件的代表价值。该成员将被编译为静态成员。&#34;这就是除了静态方法(如lukegv和Fyodor Soikin所述)之外,这些属性不可用的原因。

因此,情况似乎如下:编译的FSharpOption类型没有任何IsNone和IsSome属性。 F#中的幕后工作正在进行,以启用模拟这些属性的功能。

我知道我可以通过使用Microsoft.FSharp.Core中的OptionModule来解决这个问题。但是,似乎这个功能是F#核心库的架构师有意识的选择。选择的原因是什么?并且正在使用OptionModule正确的解决方案,还是有更好的方法来使用C#中的FSharpOption<T>类型?

2 个答案:

答案 0 :(得分:10)

这与a1.sources = r1 r2 a1.sinks = k1 k2 a1.channels = c1 c3 #sources a1.sources.r1.type=netcat a1.sources.r1.bind=localhost a1.sources.r1.port=4444 a1.sources.r2.type=exec a1.sources.r2.command=tail -f /opt/gen_logs/logs/access.log #sinks a1.sinks.k1.type=hdfs a1.sinks.k1.hdfs.path=/flume201 a1.sinks.k1.hdfs.filePrefix=netcat- a1.sinks.k1.rollInterval=100 a1.sinks.k1.hdfs.fileType=DataStream a1.sinks.k1.hdfs.callTimeout=100000 a1.sinks.k2.type=hdfs a1.sinks.k2.hdfs.path=/flume202 a1.sinks.k2.hdfs.filePefix=execCommand- a1.sinks.k2.rollInterval=100 a1.sinks.k2.hdfs.fileType=DataStream a1.sinks.k2.hdfs.callTimeOut=100000 #channels a1.channels.c1.type=file a1.channels.c1.checkpointDir=/home/cloudera/alpha/001 a1.channels.c3.type=file a1.channels.c3.checkpointDir=/home/cloudera/beta/001 #bind r1 c1 k1 a1.sources.r1.channels=c1 a1.sinks.k1.channel=c1 a1.sources.r2.channels=c3 a1.sinks.k2.channel=c3 的编译方式有关。 Channel closed [channel=c3]. Due to java.io.IOException: Cannot lock /home/cloudera/.flume/file-channel/data. The directory is already locked. [channel=c3] 值可以直接编译为创建类的实例并将值包装在其中。但option值并非真正的值,它们只是Some

试试这个:

None

(这也是nullwill show up as null in the debugger's Watch window

的原因

这是一种在运行时节省大量周期的优化。 (并且您可以通过应用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,因此您无法在这些值上使用属性或方法。如果值恰好是CompilationRepresentationFlags.UseNullAsTrueValue,那么你就会崩溃。这就是为什么你应该总是使用null进行所有操作。

至于为什么这些属性不会出现在intellisense中 - 那是因为它们是null。虽然我不确定他们为什么会在那里出现。也许是编译器工件。

答案 1 :(得分:3)

我不熟悉F#,但由于它都是CLR,我的答案来自C#的观点:

在生成的类定义中,IsNoneIsSome都是静态的,因此无法通过实例optionType访问(既不通过IntelliSense也不通过代码)。属性Value不是静态的,可以访问。