5.4.3 值的类型推断

5.4.3 值的类型推断

一般情况下,类型推断是一种机制,从代码推断出类型。其目的为了简化代码,消除了显式指定所有类型的需要。在本节中,我们会看到值的类型推断,使创建值更容易,不需要写出它们的类型。这并不是类型推断出现的唯一地方——尤其是在 F# 中——这是描述类型推的断第一部分。我们将在下一章中讨论函数(和方法)的类型推断,以及自动泛型化。

C# 3.0 中的类型推断

在 C# 中,值类型推断主要由 var 关键字表现,这是 C# 3.0 中的新功能。我们已经看到,清单 5.12 显示了几个例子,我们可以更详细地讨论一下。

Listing 5.12 Type inference using the var keyword (C#)

var num = 10 + (2 * 16);
var str = String.Concat(new string[] {"Hello ", "world!"});
var unk = null;

类型推断机制是看赋值运算符的右边,计算出表达式的类型。即使你不使用 var,也要这样做,以确保要赋给的变量兼容于要赋的值。在最后一个示例中,C# 编译器拒绝推断类型,并报告一条错误消息。空文本可以隐式转换成任何 .NET 引用类型(甚至是一个可空的值类型),而它本身并没有真正的 .NET 类型。编译器不知道我们想给变量 unk 哪种类型,所以,必须显式指定类型。我们早先使用过带选项类型的 var 关键字,那么,我们来详细分析几个示例:

var s1 = Option.Some<int>(10); <br>var s2 = Option.Some(10); <br>var n1 = Option.None<int>(); <br>var n2 = Option.None();</int></int>

第一和第三行并不令人吃惊,我们调用泛型方法,并显式指定其类型参数值,因此,编译器可以推断返回类型。在第二行,我们没有指定方法的类型参数值,但编译器知道第一个参数值的类型必须与类型参数值兼容,因此,正确推断要创建 Option<int> [2] 类型的值。对最后一行,我们得到错误,说"方法 '… …' 的类型参数的不能从用法推断。"这是因为,在这里,编译器没有足够的线索,知道应该是什么类型。</int>

在 C# 中的类型推断在很多方面有限制,但它仍然是非常有用。在 F# 中,算法更聪明,在更多的情况下,可以推断出类型,让我们看一些 F# 示例。

F# 中的类型推理

在 F# 中,我们可以经常写大段的代码,而无需显式指定任何类型,因为类型推理机制更复杂。创建值,我们使用 let 关键字,事实上,我们没有见过任何示例,在使用 let 写的值绑定中,需要显式指定其类型。清单 5.13 显示了一些示例,可能希望工作。

Listing 5.13 Type inference for basic values (F#)

let num = 123
let tup = (123, "Hello world")
let opt = Some(10)
let input = printfn "Calculating..."
if (num = 0) then None
else Some(num.ToString())

第一个示例声明了一个的基本类型 int 值。第二种情况中,我们使用元组值构造函数,所以我们会得到一个 int * string 类型的值。在清单 5.13 中,接下来的两个绑定创建选项类型的值,更具体地说,是 int 选项与 string 选项。

这些绑定之外,只有最后一个例子是特别有趣或令人吃惊。我们已经知道,F# 中的一切都是一个表达式,因此,类型推断不必要使用任何 F# 表达式(即,任何 F# 代码,因为一切都是表达式)。在这种情况下,我们的代码,首先打印一些内容到屏幕,然后,使用条件表达式返回选项类型。注意,在 F# 的轻量化语法中,空格是有意义的,因此,这个 if 表达式的起始位置与 printfn 调用的偏移量相同。

F# 编译器将看到,赋给 input 的值是从条件分支中返回的。从真的分支中,可以看到,该类型是泛型 'a 选项类型 (因为我们将返回 None),但它并不知道类型实例化是什么。这要从假分支来推断,将返回包含字符串的 Some 值。

我们提到过,F# 类型推断更复杂,所以,我们现在看一些稍微复杂的示例:

> let (n : option<system.random>) = None;; <br>val n : System.Random option</system.random>

> let n = (None : option<system.random>);; <br>val n : System.Random option</system.random>

> let n = None;;
val n : 'a option

前两个示例显示了两种不同的方法,用于添加类型注释。通常,,可以将类型批注任何 F# 代码块(如果需要)附近。下一个示例更有趣,F# 无法推断出值的完整类型。它知道我们要创建一个泛型选项类型值,但它并不知道我们想要使用什么泛型类型参数。有趣的是,这并不会导致错误,F# 创建一个泛型值。没有此构造的 C# 等效,它是只有部分指定类型的值。而不是具体类型(如 int 或 System.Object),F# 使用类型参数(可以看到,F# 自动使用以 a 开头命名的字母类型参数),其类型则由稍后使用的值完全指定。例如,我们可以比较有不同选项值的类型的值,而不会收到错误的:

> Some("testing...") = n;;
val it : bool = false

> Some(123) = n;;
val it : bool = false

现在,我们知道如何声明和创建泛型值,就应该讨论一下如何使用它们来编写函数了!有关详细的泛型函数以后再说,现在,有一个例子将会吊起你的胃口。

----------

[2] 这不是唯一有效的类型,例如,我们可能想用 Option<long>。在 C# 3.0 中,有泛型方法的类型推断规则漫长而复杂,但是在编译器愿意执行推断的情况下,它通常会得到所需的结果。</long>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值