我在F#中实现了一个C#接口,它看起来像:
public interface IThings
{
Stream ThingsInAStream()
}
Run Code Online (Sandbox Code Playgroud)
我的实现看起来像:
type FSharpThings() =
interface IThings with
member this.ThingsInAStream() =
let ms = new MemoryStream()
// add things to stream
ms
Run Code Online (Sandbox Code Playgroud)
现在我收到消息:
The expression was expected to have type
Stream
but here has type
MemoryStream
Run Code Online (Sandbox Code Playgroud)
我不明白MemoryStream 是一个Stream我知道我可以把它像流一样:
ms :> Stream
Run Code Online (Sandbox Code Playgroud)
同样的[|"string"|]
,IEnumerable<string>
它实现了接口,我可以显式地转换它,但它不能自动工作.
为什么这样做?
let things:(IEnumerable<'a> -> 'a) = (fun f -> f.First())
let thing= things([|"";""|])
Run Code Online (Sandbox Code Playgroud)
这也是自动向上转换!
Tom*_*cek 12
我认为Nicolas的答案一般都是正确的.允许在语言的任何地方自动向上转换会导致类型推断出现问题.
原则上,编译器可以尝试查找在不同分支中返回的类型的公共基类型,但这并不像听起来那么容易:
首先,它应该返回最具体的类型还是其他类型?(编译器可以找到最具体的,但实际上您可能希望返回比从代码中推断的更通用的东西 - 因此明确指定它是有用的.)
其次,界面变得困难.想象一下,两个分支返回两个不同的类,它们都实现接口IA
和IB
.编译器如何决定返回类型是否应该IA
或者IB
,也许obj
?(这是一个很大的问题,因为它会显着影响代码的使用方式!)有关详细信息,请参阅此代码段.
但是,有一个地方这不是问题,F#编译器允许它.也就是说,当将参数传递给函数或方法时 - 在这种情况下,编译器知道所需的类型是什么,因此它只需要检查是否允许upcast; 它不需要推断插入什么向上转换.因此,类型推断不受影响,因此编译器可以插入upcast.这就是为什么以下工作:
// The example from the question
let first (items:seq<'a>) = items |> Seq.head
let thing = first [|"";""|]
// Even simpler example - passing string as object
let foo (a:obj) = a
foo "123"
Run Code Online (Sandbox Code Playgroud)
这里,参数是array<string>
和函数所期望的seq<string>
.编译器知道要插入的upcast(因为它知道目标类型),所以它就是这样做的.
nic*_*las 11
这是具有强大的类型推断机制的对手:通过使一切都明确,编译器更容易推断出什么是真的.
起初感觉很奇怪,因为我们非常依赖其他轻松语言中的转换.
但实际上它总体上是一种强度,并允许所提到的类型推断,以及促进良好的编程实践,如编程到接口VS具体实现.
一个有用的构造,在它感觉简单多余的情况下,是添加演员
//The cast will be determined by the compiler, because of _
result :> _
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1034 次 |
最近记录: |