小窍门
开发软件的新手? 首先开始 学习入门 教程。 在进行转换选择之前,先练习基本类型。
是否在其他语言中有经验? C# 转换的工作方式与大多数静态类型的语言类似:扩大转换是隐式转换,收缩转换需要显式强制转换,而解析文本在面向用户的代码中应首选 TryParse。
编写 C# 代码时,通常会将值从一种类型转换为另一种类型。 例如,你可以将 int 转换为 long,读取文本并将其转换为数字,或将基类型转型为派生类型。
了解关键术语:
- 转换是将值从一种类型更改为另一种类型的过程。
-
强制转换是用于使用
(int)value等括号编写的转换的显式语法。 - 隐式转换是编译器可以保证安全时自动发生的转换。
- 显式强制转换是采用代码编写的转换,指示转换可能会丢失信息或失败。
将转换样式与情况匹配:
- 当编译器可以保证安全性时,会自动进行隐式转换,无需语法。
- 当数据可能丢失或转换可能失败时,需要显式转换。
- 在需要可能不会成功的安全引用类型转换时,会应用模式匹配或
as。 - 当源值为文本时,分析 API 适用。
隐式和显式数值转换
隐式转换始终成功。 显式转换可能会失败或丢失信息。
int itemCount = 42;
long widened = itemCount; // Implicit conversion.
double average = 19.75;
int truncated = (int)average; // Explicit cast.
Console.WriteLine($"widened: {widened}, truncated: {truncated}");
显式强制转换告知读取器转换可能会丢失信息。 在示例中, double 值在转换为 int时被截断。
有关完整转换表,请参阅 内置数值转换。
转换引用
类 是引用类型。 对它们进行强制转换不会复制数据。 它们更改了查看同一对象的方式。
某些引用转换是隐式的。 编译器保证它们是安全的。 三种情况始终生成隐式引用转换:将派生类实例分配给基类变量(派生类型是基类型的子类型),将引用类型实例分配给类型实现的接口的变量,并将任何引用类型分配给变量 object 。
Mammal otter = new() { Name = "River otter" };
Animal animal = otter; // Derived to base class: always safe.
INamed named = otter; // Reference type to implemented interface: always safe.
object obj = otter; // Any reference type to object: always safe.
Console.WriteLine(named.Name);
从基类型返回到派生类型的另一个方向需要显式检查,因为对象实际上可能不是所需的派生类型。 首选模式匹配,以便测试和赋值同时进行。
Animal knownAnimal = new Mammal { Name = "River otter" };
if (knownAnimal is Mammal mammal)
{
Console.WriteLine($"Pattern match succeeded: {mammal.Name}");
}
Animal unknownAnimal = new Reptile();
Console.WriteLine($"Can treat as mammal: {unknownAnimal is Mammal}");
模式匹配将成功的强制转换变量保持在最小的有效范围内,从而提高可读性。
如果需要可为 null 的结果而不是条件分支,请使用 as:
object boxedMammal = new Mammal { Name = "Sea lion" };
Mammal? maybeMammal = boxedMammal as Mammal;
Console.WriteLine(maybeMammal is null ? "Not a mammal" : maybeMammal.Name);
object boxedReptile = new Reptile();
Mammal? noMammal = boxedReptile as Mammal;
Console.WriteLine(noMammal is null ? "Safe null result" : noMammal.Name);
仅对引用类型和可为 null 的值类型使用 as 。 转换失败时返回 null 。
了解装箱和拆箱
装箱可以将结构或其他值类型转换为 object 或实现的接口类型。 取消装箱将从该对象引用中提取值类型。
int temperature = 72;
object boxedTemperature = temperature; // Boxing.
int unboxedTemperature = (int)boxedTemperature; // Unboxing.
Packet packet = new(7);
ILabelled labelledPacket = packet; // Boxing through an interface reference.
Console.WriteLine($"Unboxed: {unboxedTemperature}, Label: {labelledPacket.Label}");
装箱会在托管堆上分配内存,而取消装箱需要类型检查。 在热路径中,避免使用不必要的装箱,因为这会增加分配和额外工作。
使用 Parse 和 TryParse 分析文本
转换用户输入或文件内容时,从 TryParse 开始。 这可避免预期无效输入导致的异常,并使故障处理变为显式进行。 所有分析 API 都从源字符串创建新的对象或值类型实例;它们不会修改源。
string textValue = "512";
int parsed = int.Parse(textValue);
string userInput = "12x";
bool parsedSuccessfully = int.TryParse(userInput, out int safeValue);
Console.WriteLine($"parsed: {parsed}");
Console.WriteLine(parsedSuccessfully ? $"safe value: {safeValue}" : "Input is not a valid number.");
当保证输入有效(如受控测试数据)时使用 Parse 。 请使用 TryParse 表示用户输入、网络有效负载和文件数据。
核心转换 API
在日常代码中最常使用这些 API:
如需了解高级转换行为和所有重载,请查阅特定目标类型的 API 参考。