.net - F# 和自动向上转换 : sometimes it does, 有时不会,这背后的基本原理是什么?

标签 .net string f#

最近我试了一下 F# 中自动向上转换的可能性,我知道 F# 强制是显式的,但是我注意到在某些情况下实际上有点存在向上转换。

下面是一些代码,显示了向上转换工作的情况和不工作的情况:

type NameValue(name: string, value: obj) = class end
type GenNameValue<'T>(name: string, value: 'T) = class end
type MoreNameValue<'Name, 'Value>(name: 'Name, value: 'Value) = class end

let takeObjList (source: obj list) = printfn "%A" source
let takeNameValueList (source: NameValue list) = printfn "%A" source
let takeGenNameValueList (source: GenNameValue<obj> list) = printfn "%A" source
let takeMoreNameValueList (source: MoreNameValue<string, obj> list) = printfn "%A" source
let takeAnonymousList (source: {| Name: string; Value: obj |} list) = printfn "%A" source
let takeStructAnonymousList (source: struct {| Name: string; Value: obj |} list) = printfn "%A" source

let takeTupleList (source: (string * obj) list) = printfn "%A" source
let takeStructTupleList (source: struct (string * obj) list) = printfn "%A" source
let takeValueTuples (source: ValueTuple<string, obj> list) = printfn "%A" source
let takeRefTuples (source: Tuple<string, obj> list) = printfn "%A" source

// Implicit upcasting to obj => works
takeObjList [42; "middle"; 16us]
takeNameValueList [NameValue("a", "c"); NameValue("b", 42)]
takeGenNameValueList [GenNameValue<obj>("a", "c"); GenNameValue<obj>("a", 42)]
takeMoreNameValueList [MoreNameValue<string, obj>("a", "c"); MoreNameValue<string, obj>("a", 42)]
takeAnonymousList [{| Name = "a"; Value = "c" |}; {| Name = "b"; Value = 42 |}]
takeStructAnonymousList [{| Name = "a"; Value = "c" |}; {| Name = "b"; Value = 42 |}]

// Implicit upcasting to obj => doesn't work
//  [FS0001] This expression was expected to have type 'obj' but here has type 'string'
//  [FS0001] This expression was expected to have type 'obj' but here has type 'int'
takeTupleList [("a", "c"); ("b", 42)]
takeStructTupleList [("a", "c"); ("b", 42)]
takeValueTuples [ValueTuple.Create("a", "c"); ValueTuple.Create("a", 42)]
takeRefTuples [Tuple.Create("a", "c"); Tuple.Create("a", 42)]

此外,我还注意到单独向上转换元组是可行的,但当它是一个集合时会立即失败:

let build1 a b: (string * obj) list = [a; b]
let build2 a: (string * obj) list = a

// Does work
build1 ("a", "c") ("a", 42) |> ignore
// Does not work
build2 [("a", "c"); ("a", 42)] |> ignore

我想知道背后的原理。恕我直言,它看起来有点不一致,在某些情况下可以,在另一些情况下则不能。我可能遗漏了什么,我想知道是什么。

最佳答案

C# 具有隐式协变和逆变。 这使您可以自动转换 IEnumerable<string>IEnumerable<object> ,或使用 Action<object>int .

F# 也没有。 Vote here! .

所以你不能写:

[1; 2; 3] :> obj list //compile error

您看到的其余部分是尝试选择最佳匹配类型的类型推断。

let printlist (list: obj list) = 
    list |> List.iter(printfn "%A")

printlist [1; 2; 3] //compiles - literals will be inferred using type annotation
let intlist = [1; 2; 3]
printlist intlist //this will not compile

请注意,您所有的失败案例都是针对非同构集合或协变集合。

目前,F# 具有灵活的类型,本质上是用于以下方面的语法糖:

#T = 'a where 'a :> T

它让您的协方差有限。

let printlist (list: #obj list) = 
    list |> List.iter(printfn "%A")

let intlist = [1; 2; 3]
printlist intlist //compiles

我相信其他人可以提供更多背景信息。

关于.net - F# 和自动向上转换 : sometimes it does, 有时不会,这背后的基本原理是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61723770/

相关文章:

.net - 'System.Collections.Generic.IList<object >' does not contain a definition for ' Add' 当使用 'dynamic' 和 'ExpandoObject'

c# - 使用viewmodel在wpf窗口中实现进度条

c++ - 在 C++ 中用一个字符分隔符分割字符串

wpf - 使用 WPF 在 F# 中调用窗口加载事件的方法

f# - 在 FsCheck.XUnit 中,如何进行详细检查?

c# - 有没有我可以在属性上使用的属性来告诉 DataGridView 如何设置列的格式?

c# - ASP.NET MVC 3 - 在 View 中处理模型的最佳实践

c - C语言中如何分配子字符串?

c# - 如果是单独的单引号,则替换它

memory-leaks - F# 可观察事件是否消除、调解或与弱引用的需要无关?