C#入门系列(五)——类型转换

C#入门系列目录

C#入门系列(一)——C#简介

C#入门系列(二)——第一个C#程序

C#入门系列(三)——数据类型

C#入门系列(四)——变量与常量

C#入门系列(五)——类型转换

上一节我们介绍了变量和常量,本节我们将一起学习类型转换。

由于 C# 是在编译时静态类型化的,因此变量在声明后就无法再次声明,或无法分配另一种类型的值,除非该类型可以隐式转换为变量的类型。 例如,string 无法隐式转换为 int。 因此,在将 i 声明为 int 后,无法将字符串“Hello”分配给它,但有时可能需要将值复制到其他类型的变量或方法参数中。 例如,可能需要将一个整数变量传递给参数类型化为 double 的方法。 或者可能需要将类变量分配给接口类型的变量。 这些类型的操作称为类型转换。 在 C# 中,可以执行以下几种类型的转换:

隐式转换

对于内置数值类型,如果要存储的值无需截断或四舍五入即可适应变量,则可以进行隐式转换。 对于整型类型,这意味着源类型的范围是目标类型范围的正确子集。由于这种转换是类型安全且不会导致数据丢失,因此无需使用任何特殊语法。 例如,long 类型的变量(64 位整数)能够存储 int(32 位整数)可存储的任何值。 在下面的示例中,编译器先将右侧的 num 值隐式转换为 long 类型,再将它赋给 bigNum。

int num = 2147483647;
long bigNum = num;

下表显示内置数值类型之间的预定义隐式转换:

表 1

From

sbyte

short、int、long、float、double 或 decimal

byte

short、ushort、int、uint、long、ulong、float、double 或 decimal

short

int、long、float、double 或 decimal

ushort

int、uint、long、ulong、float、double 或 decimal。

int

long、 float、 double或 decimal

uint

long、ulong、float、double 或 decimal

long

float、double 或 decimal

ulong

float、double 或 decimal

float

double

注意

从 int、uint、long 或 ulong 到 float 的隐式转换以及从 long 或 ulong 到 double 的隐式转换可能会丢失精准率,但绝不会丢失一个数量级。 其他隐式数值转换不会丢失任何信息。

  • 任何整型数值类型都可以隐式转换为任何浮点数值类型。
  • 不存在针对 byte 和 sbyte 类型的隐式转换。 不存在从 double 和 decimal 类型的隐式转换。
  • decimal 类型和 float 或 double 类型之间不存在隐式转换。
  • 类型 int 的常量表达式的值(例如,由整数文本所表示的值)如果在目标类型的范围内,则可隐式转换为 sbyte、byte、short、ushort、uint 或 ulong:

显式转换(强制转换)

如果进行转换可能会导致信息丢失,则编译器会要求执行显式转换,显式转换也称为强制转换。 强制转换是显式告知编译器你打算进行转换且你知道可能会发生数据丢失的一种方式。 若要执行强制转换,请在要转换的值或变量前面的括号中指定要强制转换到的类型。典型的示例包括从数值到精度较低或范围较小的类型的转换和从基类实例到派生类的转换。

下面的程序将 double 强制转换为 int。如不强制转换则该程序不会进行编译。

class Test
{
    static void Main()
    {
        double x = 1234.7;
        int a;
        // Cast double to int.
        a = (int)x;
        System.Console.WriteLine(a);
    }
}

对于引用类型,如果需要从基类型转换为派生类型,则必须进行显式强制转换:

Giraffe g = new Giraffe();  
  
Animal a = g;  
 
Giraffe g2 = (Giraffe) a;

引用类型之间的强制转换操作不会更改基础对象的运行时类型;它只更改用作对该对象引用的值的类型。

下表显示不存在隐式转换的内置数值类型之间的预定义显式转换:

表 2

From

sbyte

byte、 ushort、 uint或 ulong

byte

sbyte

short

sbyte、byte、ushort、uint 或 ulong

ushort

sbyte、byte 或 short

int

sbyte、byte、short、ushort、uint 或 ulong

uint

sbyte、byte、short、ushort 或 int

long

sbyte、byte、short、ushort、int、uint 或 ulong。

ulong

sbyte、byte、short、ushort、int、uint 或 long。

float

sbyte、byte、short、ushort、int、uint、long、ulong 或 decimal

双精度

sbyte、byte、short、ushort、int、uint、long、ulong、float 或 decimal

decimal

sbyte、byte、short、ushort、int、uint、long、ulong、float 或 double

用户定义的转换

用户定义的转换是使用特殊方法执行,这些方法可定义为在没有基类和派生类关系的自定义类型之间启用显式转换和隐式转换。

用户定义类型可以定义从或到另一个类型的自定义隐式或显式转换。

隐式转换无需调用特殊语法,并且可以在各种情况(例如,在赋值和方法调用中)下发生。 预定义的 C# 隐式转换始终成功,且永远不会引发异常。 用户定义隐式转换也应如此。 如果自定义转换可能会引发异常或丢失信息,请将其定义为显式转换。

operator 和 implicit 或 explicit 关键字分别用于定义隐式转换或显式转换。 定义转换的类型必须是该转换的源类型或目标类型。 可用两种类型中的任何一种类型来定义两种用户定义类型之间的转换。

下面的示例展示如何定义隐式转换和显式转换:

using System;

public readonly struct Digit
{
    private readonly byte digit;

    public Digit(byte digit)
    {
        if (digit > 9)
        {
            throw new ArgumentOutOfRangeException(nameof(digit), "Digit cannot be greater than nine.");
        }
        this.digit = digit;
    }

    public static implicit operator byte(Digit d) => d.digit;
    public static explicit operator Digit(byte b) => new Digit(b);

    public override string ToString() => $"{digit}";
}

public static class UserDefinedConversions
{
    public static void Main()
    {
        var d = new Digit(7);
        
        byte number = d;
        Console.WriteLine(number);  // output: 7

        Digit digit = (Digit)number;
        Console.WriteLine(digit);  // output: 7
    }
}

使用帮助程序类进行转换

若要在非兼容类型(如整数和 System.DateTime 对象,或十六进制字符串和字节数组)之间转换,可使用 System.BitConverter 类、System.Convert 类和内置数值类型的 Parse 方法(如 Int32.Parse)

BitConverter示例

使用 BitConverter 类将字节数组转换为 int 然后又转换回字节数组。 例如,在从网络读取字节之后,可能需要将字节转换为内置数据类型。

将数组中的四个字节转换为 int

byte[] bytes = { 0, 0, 0, 25 };

if (BitConverter.IsLittleEndian)
    Array.Reverse(bytes);

int i = BitConverter.ToInt32(bytes, 0);

将 int 转换为字节数组

byte[] bytes = BitConverter.GetBytes(201805978);

Convert示例

using System;

public class ConvertStringExample1
{
    static void Main(string[] args)
    {
        int numVal = -1;
        bool repeat = true;

        while (repeat)
        {
            Console.Write("Enter a number between −2,147,483,648 and +2,147,483,647 (inclusive): ");

            string input = Console.ReadLine();

            // ToInt32 can throw FormatException or OverflowException.
            try
            {
                numVal = Convert.ToInt32(input);
                if (numVal < Int32.MaxValue)
                {
                    Console.WriteLine("The new value is {0}", ++numVal);
                }
                else
                {
                    Console.WriteLine("numVal cannot be incremented beyond its current value");
                }
           }
            catch (FormatException)
            {
                Console.WriteLine("Input string is not a sequence of digits.");
            }
            catch (OverflowException)
            {
                Console.WriteLine("The number cannot fit in an Int32.");
            }

            Console.Write("Go again? Y/N: ");
            string go = Console.ReadLine();
            if (go.ToUpper() != "Y")
            {
                repeat = false;
            }
        }
    }
}

Parse示例

using System;

public class StringConversion
{
    public static void Main()
    {
       string input = String.Empty;
       try
       {
           int result = Int32.Parse(input);
           Console.WriteLine(result);
       }
       catch (FormatException)
       {
           Console.WriteLine($"Unable to parse ‘{input}‘");
       }
       // Output: Unable to parse ‘‘

       try
       {
            int numVal = Int32.Parse("-105");
            Console.WriteLine(numVal);
       }
       catch (FormatException e)
       {
           Console.WriteLine(e.Message);
       }
       // Output: -105

        if (Int32.TryParse("-105", out int j))
            Console.WriteLine(j);
        else
            Console.WriteLine("String could not be parsed.");
        // Output: -105

        try
        {
            int m = Int32.Parse("abc");
        }
        catch (FormatException e)
        {
            Console.WriteLine(e.Message);
        }
        // Output: Input string was not in a correct format.

        string inputString = "abc";
        if (Int32.TryParse(inputString, out int numValue))
            Console.WriteLine(inputString);
        else
            Console.WriteLine($"Int32.TryParse could not parse ‘{inputString}‘ to an int.");
        // Output: Int32.TryParse could not parse ‘abc‘ to an int.
     }
}

装箱和拆箱

装箱是将值类型转换为 object 类型,拆箱将从object类型到值类型的显式转换。装箱是隐式的;拆箱是显式的。

示例

int i = 123;      // 值类型
object o = i;     // 装箱
int j = (int)o;   // 拆箱

总结

以上对C#中的数据类型转换进行了简单介绍,希望能对大家有所帮助,不足之处希望大家及时指出,也希望大家多提意见!

C#入门系列(五)——类型转换

欢迎关注微信公众号:CSharp开发

相关推荐