数据类型强制转换和转换
可通过多种方法执行数据类型转换或强制转换。选择哪种方法取决于你对两个重要问题的回答:
- 问题 1:对于不同的值,尝试更改值的数据类型是否可能在运行时引发异常?
- 问题 2:对于不同的值,尝试更改值的数据类型是否可能导致信息丢失?
在此练习中,我们将研究这些问题、它们的答案的含义,以及需要更改数据类型时应使用哪种方法。
问题 1:对于不同的值,尝试更改值的数据类型是否可能在运行时引发异常?
C# 编译器会尝试适应你的代码,并且会始终避免可能引发异常的操作。了解 C# 编译器的主要问题后,你将更容易理解它以特定方式工作的原因。
步骤 1 - 编写尝试添加 int 和字符串的代码,并将结果保存为 int
将以下代码添加到 .NET 编辑器:
int first = 2;
string second = "4";
int result = first + second;
Console.WriteLine(result);在此,我们尝试添加值 2 和 4。值 4 的类型为 string。这是否有效?
运行代码,应会得到以下输出。
(3,14): error CS0029: Cannot implicitly convert type 'string' to 'int'错误消息告诉我们,问题出在我们对 string 数据类型的使用上。但为什么 C# 编译器无法处理此问题?毕竟,我们可以执行反向操作,将数字连接到 string,并将其保存在字符串变量中。此处,我们只能将 result 变量的数据类型从 int 更改为 string:
int first = 2;
string second = "4";
string result = first + second;
Console.WriteLine(result);将生成以下输出:
24让我们返回到第一个代码示例,其中 result 变量的类型为 int。为什么 C# 编译器不明白我们要将包含 4 的变量 second 视为数字,而不是 string?
C# 编译器会在操作过程中发现潜在问题。变量 second 的类型为 string,因此可能会将其设置为不同的值,如 "hello"。如果 C# 编译器尝试将 "hello" 转换为数字,则会在运行时引发异常。为避免这种情况,C# 编译器不会隐式执行从 string 到 int 的转换。
从 C# 编译器的角度来看,更安全的操作是将 int 转换为 string 并改为执行串联。
如果你的目的是使用字符串执行加法,C# 编译器会要求你对数据转换过程进行更显式的控制。换言之,它会强制要求你更多地参与其中,这样你就可以采取适当的预防措施来处理转换可能引发异常的可能性。
如果需要将值从原始数据类型更改为新的数据类型,并且更改可能在运行时引发异常,则必须执行数据转换。
若要执行数据转换,你可以使用下列方法之一:
- 对数据类型使用帮助程序方法
- 对变量使用帮助程序方法
- 使用 Convert 类的方法
稍后,我们将在本单元中介绍几个用于数据转换的方法示例。
问题 2:对于不同的值,尝试更改值的数据类型是否可能导致信息丢失
术语“扩大转换”表示你正在尝试将值从一种可以保留较少信息的数据类型转换为一种可保留较多信息的数据类型。在这种情况下,你不会丢失任何信息。因此,一个示例是转换存储在 int 类型的变量中的值,并将该值转换为 decimal 类型的变量。
如果打印出这两个值,你可能会发现没有区别。
如果你了解需要执行扩大转换,则可以依赖于隐式转换。编译器处理隐式转换。
步骤 2 - 在 .NET 编辑器中修改代码,执行隐式转换
int myInt = 3;
Console.WriteLine($"int: {myInt}");
decimal myDecimal = myInt;
Console.WriteLine($"decimal: {myDecimal}");运行代码时,应会看到以下输出:
int: 3
decimal: 3此示例的关键是下面的代码行:
decimal myDecimal = myInt;由于任何 int 值都可以轻松地纳入 decimal,因此编译器会执行转换。
术语“收缩转换”表示你试图将值从一种可以保留较多信息的数据类型转换为一种可保留较少信息的数据类型。在这种情况下,你可能会丢失信息,如精度(即小数点后的位数)。一个示例是转换存储在 decimal 类型的变量中的值,并将该值转换为 int 类型的变量。如果打印出这两个值,你可能会注意到出现了信息丢失的情况。
如果你了解需要执行收缩转换,则需要执行强制转换。强制转换是一种针对 C# 编译器的指令,即你知道可能发生精度丢失,但这种情况可以接受。
步骤 3 - 在 .NET 编辑器中修改代码,执行强制转换
若要执行强制转换,请使用强制转换运算符 () 将数据类型括起来,然后将其放在要转换的变量旁边。
decimal myDecimal = 3.14m;
Console.WriteLine($"decimal: {myDecimal}");
int myInt = (int)myDecimal;
Console.WriteLine($"int: {myInt}");如果运行代码示例,应会得到以下输出:
decimal: 3.14
int: 3此示例的关键是下面的代码行:
int myInt = (int)myDecimal;变量 myDecimal 保留具有两位小数的精度值。通过添加强制转换指令 (int),我们告诉编译器 C#,我们知道可能发生精度丢失,并且在这种情况下是可以接受的。
如何确定转换是“扩大转换”还是“收缩转换”?
如果不确定是否会丢失数据,可执行以下两项操作之一。
权威资源是文章:.NET 中的类型转换表
本文介绍类型的 .NET 类库名称,而非数据类型的 C# 关键字,因此,起初可能会让人感到困惑。使用以下文章在 C# 关键字和 .NET 类库类型之间进行映射:内置类型表
但是,您也可以编写一些代码来以两种不同的方式执行转换,并观察更改。开发人员经常编写小测试来更好地理解两个相似方法的倾向。例如:
decimal myDecimal = 1.23456789m;
float myFloat = (float)myDecimal;
Console.WriteLine($"Decimal: {myDecimal}");
Console.WriteLine($"Float: {myFloat}");此代码生成以下输出:
Decimal: 1.23456789
Float: 1.234568从本例中,你可了解到将 decimal 强制转换为 float 是收缩转换,因为会丢失精度。
执行数据转换
我们之前说过,当你需要将值从一种数据类型更改为另一种数据类型,并且该更改可能引发运行时异常时,应该执行数据转换,并且有三种方法供你使用:
- 对变量使用帮助程序方法
- 对数据类型使用帮助程序方法
- 使用 Convert 类的方法
使用 ToString() 将数字转换为字符串
每个数据类型变量都具有 ToString() 方法。 ToString() 方法的作用取决于它在给定类型上的实现方式。但在大多数基元中,该方法执行扩大转换。尽管这并不是绝对必要的(因为在大多数情况下我们可以依赖于隐式转换),但它可以向其他开发者传递这样的信息,即你了解正在执行的操作,并且是有意为之的。
步骤 4 - 在 .NET 编辑器中修改代码,使用 ToString() 帮助程序方法将数字转换为字符串
下面是使用 ToString() 方法将 int 值显式转换为 string 的简单示例。
int first = 5;
int second = 7;
string message = first.ToString() + second.ToString();
Console.WriteLine(message);如果运行该命令,会得到以下输出,即这两个值的串联:
57将字符串显式转换为数字
大部分数字数据类型都具有 Parse() 方法,可将字符串转换为给定的数据类型。在这种情况下,我们使用 Parse() 方法将两个字符串转换为 int 值,然后将二者相加。
步骤 5 - 在 .NET 编辑器中修改代码,以使用 Parse() 帮助器方法将字符串转换为整数
在 .NET 编辑器中,删除或注释掉前面练习步骤中的代码,并添加以下代码:
string first = "5";
string second = "7";
int sum = int.Parse(first) + int.Parse(second);
Console.WriteLine(sum);如果运行代码示例,应会得到以下输出:
12你能否发现上述代码示例的潜在问题?如果将 first 或 second 变量设置为无法转换为 int 的值,会发生什么情况呢?会在运行时引发异常。这正是 C# 编译器和运行时希望我们提前做好规划的情况。幸运的是,我们可以通过多种方式缓解此情况。
缓解此情况最简单的方法是使用 Parse()这是比 TryParse() 方法更好的版本。我们将在下一单元进行介绍。
使用 Convert 类进行数据转换
Convert 类有许多可用于将值从一种类型转换为另一种类型的帮助程序方法。在下面的代码示例中,我们将两个字符串转换为 int 类型的值。
步骤 6 - 在 .NET 编辑器中修改代码,使用 Convert 类将字符串转换为数字
在 .NET 编辑器中,删除或注释掉前面练习步骤中的代码,并添加以下代码:
string value1 = "5";
string value2 = "7";
int result = Convert.ToInt32(value1) * Convert.ToInt32(value2);
Console.WriteLine(result);如果运行代码示例,应会得到以下输出:
35备注
为什么该方法的名称为 ToInt32()?为什么不是 ToInt()? System.Int32 是 .NET 类库中的基础数据类型名称,C# 编程语言将其映射到 int 关键字。由于 Convert 类也属于 .NET 类库,因此调用该类时是按其全名(而非按其 C# 名称)进行调用。通过将数据类型定义为 .NET 类库的一部分,Visual Basic、F#、IronPython 等多种 .NET 语言可以在 .NET 类库中共享相同的数据类型和相同的类。在本模块末尾,我们将发布一些链接,方便你了解有关 .NET 通用类型系统的详细信息。
ToInt32() 方法具有 19 个重载版本,这让它能够接受几乎所有的数据类型。我们稍后会进行介绍。
我们在此处使用带有字符串的 Convert.ToInt32() 方法,但你应该尽可能使用 TryParse()。
那么,在什么情况下我们应该使用 Convert 类? Convert 类非常适用于将小数数字转换为整数 (int),因为它按预期方式向上舍入。
强制转换与转换
下面的示例演示如果尝试以收缩转换方式将 decimal 强制转换为 int,而不是使用 Convert.ToInt32() 方法将该 decimal 转换为 int,会发生什么情况。
步骤 7 - 在 .NET 编辑器中修改代码,比较将 decimal 强制转换和转换为 int 的区别
在 .NET 编辑器中,删除或注释掉前面练习步骤中的代码,并添加以下代码:
int value = (int)1.5m; // casting truncates
Console.WriteLine(value);
int value2 = Convert.ToInt32(1.5m); // converting rounds up
Console.WriteLine(value2);在运行代码时,将获得以下输出:
1
2强制转换时,系统会截断 float 的值,这意味着完全忽略 decimal 后的值。我们可以将文本 float 更改为 1.999m,强制转换的结果也是相同的。
使用 Convert.ToInt32() 进行转换时,文本 float 值将正确地向上舍入到 2。如果将文本值更改为 1.499m,则会向下舍入到 1。通过在 .NET 编辑器中更改值来尝试下。
回顾
我们介绍了有关数据转换和强制转换的多个重要概念:
- 如果执行强制转换可能会导致运行时错误,请执行数据转换。
- 执行显式强制转换,告诉编译器你了解存在丢失数据的风险。
- 执行扩大转换时,依靠编译器执行隐式转换。
- 使用 () 强制转换运算符和数据类型执行强制转换(例如
(int)myDecimal)。 - 如果要执行收缩转换,但要执行舍入,而不是信息截断,请使用
Convert类。