C# 类型转换

类型转换从根本上说是类型铸造,或者说是把数据从一种类型转换为另一种类型。在 C# 中,类型铸造有两种形式:

  • 隐式类型转换 - 这些转换是 C# 默认的以安全方式进行的转换, 不会导致数据丢失。例如,从小的整数类型转换为大的整数类型,从派生类转换为基类。
  • 显式类型转换 - 显式类型转换,即强制类型转换。显式转换需要强制转换运算符,而且强制转换会造成数据丢失。

值类型间的转换

在C#的运算中,要求各被运算单元的数据类型是一致的,且运算结果将与被运算单元保持一致。
例如:10/4 的运算中,被运算的两个数字10与4都属于int 类型,则运算结果也应该是int类型的,所以运算结果是2,而不是2.5。实际开发过程中,对数据的单纯操作可能无法满足现有的需求,这时就需在不同数据类型之间进行相应的转换。C# 提供了2种转换方式:

  1. 自动类型转换(又叫:隐式类型转换 或 向上转型)
  2. 强制类型转换(又叫:显示类型转换 或 向下转型)

自动类型转换

自动类型转换,又叫做:隐式转型 或 向上转型。是 C# 默认的以安全方式进行的转换, 不会导致数据丢失。
自动转型常见的场景:
 当被运算单元的数据类型不同时,程序将尝试可行的自动转换(提升)数据类型。关于自动转型的另外一种说法是:将一个范围小的数据转为较大类型的数据时,系统会自动进行类型转换
例如:15000 * 0.25 的运算实际为 int 与 double 的运算,程序执行时,会将 15000 按照 double 类型处理,以保证运算结果是准确的。

强制类型转换

  • 很明显,上两页练习中的程序代码是存在错误的。如何解决上面的问题呢? 就需要使用:强制类型转换
    强制类型转换,又叫向下转型。指的是将一个范围大的数据转为一个范围小的数据类型。
  • 基本语法:( 要转换成的类型 ) 具体要转换的数据或变量
  • 例如: ( int ) 3.1415926 表示将double 类型的数据3.1415926 强制转换为 int 类型,结果为: 3 。
  • 强制转型有风险,有可能引起数据溢出或精度丧失,造成最后数据结果不准确的情况。所以:一般情况下,不推荐使用强制类型转换方式。
  • 例如:

 

1
2
int  i  =  256 ;
byte b = ( byte ) i ;

上面两行代码中,声明 int 型变量 i 值256,然后将 i 的值赋值给 byte 型变量 b,打印b时结果为:0 ,出现这种结果是因为byte(1个字节)在内存中存储4个字节的int型变量时无法容纳,故出现数据溢出情况,导致运算结果不准确。

隐式转换和显式转换

隐式转换:C# 默认的以安全方式进行的转换。本质是从小存储容量数据类型自动转换为大存储容量数据类型,从派生类转换为基类。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
namespace TypeConvertion
{ class Class1
{

}

class Class2 : Class1 //类Class2是类Class1的子类
{

}
class Program
{
static void Main(string[] args)
{
int inum = 100;
long lnum = inum; // 进行了隐式转换,将 int 型(数据范围小)数据转换为了 long 型(数据范围大)的数据
Class1 c1 = new Class2(); // 这里也是隐式转换,将一个新建的 Class2 实例转换为了其基类 Class1 类型的实例 C1
}
}
}

显式转换:通过用户使用预定义的函数显式完成的,显式转换需要强制转换运算符。

转换类型的范围大小和从属关系和隐式转换相反。显式转换可能会导致数据出错,或者转换失败,甚至无法编译成功。

实例:

1
2
3
4
5
6
7
double dnum = 100.1;
int ifromd = (int)dnum; //double类型显式转换转为int类型

Class1 c11 = new Class1();
Class2 c22 = c11 as Class2; //使用as进行显式转换
Console.WriteLine(c22 is Class1);
Console.WriteLine(c22 is Class2);
  • 运行结果:
1
2
FALSE
FALSE

C# 类型转换方法(Convert类)

方法 描述
ToBoolean 如果可能的话,把类型转换为布尔型。
ToByte 把类型转换为字节类型。
ToChar 如果可能的话,把类型转换为单个 Unicode 字符类型。
ToDateTime 把类型(整数或字符串类型)转换为 日期-时间 结构。
ToDecimal 把浮点型或整数类型转换为十进制类型。
ToDouble 把类型转换为双精度浮点型。
ToInt16 把类型转换为 16 位整数类型。
ToInt32 把类型转换为 32 位整数类型。
ToInt64 把类型转换为 64 位整数类型。
ToSbyte 把类型转换为有符号字节类型。
ToSingle 把类型转换为小浮点数类型。
ToString 把类型转换为字符串类型。
ToType 把类型转换为指定类型。
ToUInt16 把类型转换为 16 位无符号整数类型。
ToUInt32 把类型转换为 32 位无符号整数类型。
ToUInt64 把类型转换为 64 位无符号整数类型。

xxx.Parse

xxx.Parse就是把String转换成int,char,double....等,也就是xxx.Parse(string) 括号中的一定要是string。

类型之间的转换 - Convert 和 Parse

1
2
3
4
5
6
7
8
9
10
int a = 123;
string locstr = a.ToString();

//如果要将"locstr"转成整型数

//方法一: 用 Convert
int i = Convert.ToInt16(locstr);

//方法二: 用 Parse
int ii = int.Parse(locstr);

xxx.TryParse(string s,out xxx i)

1
int.TryParse(string s,out int i)

该方式也是将数字内容的字符串转换为int类型,但是该方式比int.Parse(string s) 好一些,它不会出现异常,最后一个参数result是输出值,如果转换成功则输出相应的值,转换失败则输出0。

1
2
3
4
5
6
7
8
9
10
11
12
13
class test
{
static void Main(string[] args)
{
string s1="abcd";
string s2="1234";
int a,b;
bool bo1=int.TryParse(s1,out a);
Console.WriteLine(s1+" "+bo1+" "+a);
bool bo2=int.TryParse(s2,out b);
Console.WriteLine(s2+" "+bo2+" "+b);
}
}

结果输出:

1
2
abcd False 0
1234 True 1234

浅谈 string 转 int 与抛异常

string 字符串类型和 int 也是可以转换的。下一行的代码给出错误的转换方法。

1
2
string a = "123";  // 将a设置为字符串“123”
int x = (int) a; // 转换

上述代码,毋庸置疑,肯定是错误的。VS 在编译时就过不了。那么,string 该怎么转换成 int 呢?

这里,我们需要用到 int.Parse(),核心代码为:

1
2
string a = "123";     // 将a设置为字符串“123”
int x = int.Parse(a); // 转换

如果仅仅是这样,是没有问题的,但是,我们下面再来做一个实例。

用户输入一个数字,而电脑将计算出这个数字加上1以后的答案,并且显示出来。

用户输入的东西,即 Console.ReadLine() ,一定是以字符串形式表现的。

于是,运用之前的方法,我们可以写出以下的代码:

1
2
3
4
5
6
7
8
9
10
class 测试
{
static void Main(string[] args)
{
Console.WriteLine("输入数字,将计算出它加一的答案");
int a = int.Parse(Console.ReadLine()); //转换用户输入的数字
Console.WriteLine("答案是{0}",++a); //++a 即 a+1 后的那个数,将它输出出来
Console.ReadKey();
}
}

当程序运行时,会出现:

1
2
3
输入数字,将计算出它加一的答案
3
答案是4

这样就很完美了吗?不!!

如果用户输入并非数字的其他字符,如汉字,会发生什么情况?

此时,用户输入 ,显然,程序将无法继续运行下去,因为int类型只能存整数,不能存字符。

这时,程序就会抛出异常。

如果用 VS 编,你还会看到异常类型:FormatException。

所以,为了保险,可以用try、catch来解决此问题。核心代码为:

1
2
3
4
5
6
7
8
try
{

}
catch (Exception)
{

}

try 在英语中就是尝试的意思。在这段代码中,try{} 部分,顾名思义,也就是去尝试进行下面的代码。catch{} 部分,则是检测异常。这样,在出现异常时,catch 就能捕获到异常,从而程序并不会停止。

则这段程序,完整的代码应该为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;
class 测试
{
static void Main(string[] args)
{
try
{
Console.WriteLine("输入数字,将计算出它加一的答案");
int a = int.Parse(Console.ReadLine()); //有可能会抛出异常
Console.WriteLine("答案是{0}",++a); //如果没有异常,程序才会进入这一步
}
catch (Exception)
{
Console.WriteLine("无法转换"); //如果捕获到异常,就说“无法转换”
}
Console.ReadKey();
}
}

这样,如果我输入了 ,程序结果为:

1
无法转换

数据精度

(int)xxx

例如: ( int ) 3.1415926 表示将double 类型的数据3.1415926 强制转换为 int 类型,结果为:3

Convert.ToInt32() 与 int.Parse() 的区别

没搞清楚 Convert.ToInt32int.Parse() 的细细微区别时千万别乱用,否则可能会产生无法预料的结果.
举例来说:假如从 url 中取一个参数 page 的值,我们知道这个值是一个 int,所以即可以用 **Convert.ToInt32(Request.QueryString[“page”])**,也可以用 **int.Parse(Request.QueryString[“page”])**,但是如果 page 这个参数在 url 中不存在,那么前者将返回 0,0 可能是一个有效的值,所以你不知道 url 中原来根本就没有这个参数而继续进行下一下的处理,这就可能产生意想不到的效果,而用后一种办法的话没有 page 这个参数会抛出异常,我们可以捕获异常然后再做相应的处理,比如提示用户缺少参数,而不是把参数值当做 0 来处理。

  1. 这两个方法的最大不同是它们对 null 值的处理方法: Convert.ToInt32(null) 会返回 0 而不会产生任何异常,但 int.Parse(null) 则会产生异常。

  2. 对数据进行四舍五入时候的区别

    1. Convert.ToInt32(double value) 如果 value 为两个整数中间的数字,则返回二者中的偶数;即 3.5 转换为 4,4.5 转换为 4,而 5.5 转换为 6。不过 4.6 可以转换为 5,4.4 转换为 4 。
    2. int.Parse(“4.5”) 直接报错:**”输入字符串的格式不正确”**。
    3. int(4.6) = 4 Int 转化其他数值类型为 Int 时没有四舍五入,强制转换。
  3. 对被转换类型的区别 int.Parse 是转换 String 为 int, Convert.ToInt32 是转换继承自 Object 的对象为 int 的(可以有很多其它类型的数据)。你得到一个 object 对象, 你想把它转换为 int, 用 int.Parse 就不可以, 要用 Convert.ToInt32

Convert.ToInt32() 、int.TryParse() 和 int.Parse()

  1. 对于转换对象,Convert.ToInt32() 可以为多种类型(例出数字类型外 bool,DateTime 等),int.TryParse() 和 int.Parse() 只能是整型字符串类型(即各种整型 ToString() 之后的形式,不能为浮点型,否则 int.Parse() 就会出现输入的字符串格式不正确的错误,int.TryParse() 也会返回 false,输出参数为 0 ,(int)只能是数字类型(例 float,int,uint等);

  2. 对于空值 NULL,从运行报错的角度讲,(int) 强制转换和 int.Parse() 都不能接受 NULL;Convert.ToInt32() 其实是在转换前先做了一个判断,参数如果为 NULL,则直接返回 0,否则就调用 int.Parse() 进行转换,int.TryParse() 其实是对 int.Parse() 做了一个异常处理,如果出现异常则返回 false,并且将输出参数返回 0;

  3. 针对于浮点型的取舍问题,浮点型只有 Convert.ToInt32() 和 (int) 能进行转换,但是也是进行取舍了的,Convert.ToInt32() 采取的取舍是进行四舍五入,而 (int) 则是截取浮点型的整数部分,忽略小数部分,例如 Convert.ToInt32(1.499d) 和 (int)1.499d 都返回 1,Convert.ToInt32(1.5d) 返回 2,而 (int)1.5d 还是返回 1;

  4. 关于溢出,将大的数据类型转换为小的数据类型时 Convert.ToInt32() 和 int.Parse() 都会报溢出错误,值对于 Int32 太大或太小,而 (int) 不报错,但是返回值为 -1。

如此可见,我们在进行数据转换前选择转换方法要谨慎,如果是数字类型可以考虑直接用(int)强制转换,如果是整型字符串类型的,考虑用 int.Parse() 进行转换,如果不是这两种类型,再考虑用 Convert.ToInt32() 进行转换。

Convert.ToDouble与Double.Parse的区别。

实际上 Convert.ToDouble 与 Double.Parse 较为类似,实际上 Convert.ToDouble内部调用了 Double.Parse:**

对于参数为null的时候:

  • Convert.ToDouble参数为 null 时,返回 0.0;
  • Double.Parse 参数为 null 时,抛出异常。

对于参数为””的时候:

  • Convert.ToDouble参数为 “” 时,抛出异常;
  • Double.Parse 参数为 “” 时,抛出异常。

其它区别:

  • Convert.ToDouble可以转换的类型较多;
  • Double.Parse 只能转换数字类型的字符串。
  • Double.TryParse 与 Double.Parse 又较为类似,但它不会产生异常,转换成功返回 true,转换失败返回 false。最后一个参数为输出值,如果转换失败,输出值为 0.0。

附测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
try
{
//string a = "0.2";
//string a = null;
string a = "";
try
{
double d1 = Double.Parse(a);
}
catch (Exception err)
{
Console.WriteLine("d1转换出错:" + err.Message);
}

try
{
double d2 = Convert.ToDouble(a);
}
catch (Exception err)
{
Console.WriteLine("d2转换出错:" + err.Message);

}
try
{
double d3;
Double.TryParse(a,out d3);
}
catch (Exception err)
{
Console.WriteLine("d3转换出错:" + err.Message);
}
}
finally
{
Console.ReadKey();
}

}
}
}

装箱和拆箱

  • 装箱:值类型转换为对象类型, 实例:
1
2
int val = 8;
object obj = val;//整型数据转换为了对象类型(装箱)
  • 拆箱:之前由值类型转换而来的对象类型再转回值类型, 实例:
1
2
3
int val = 8;
object obj = val;//先装箱
int nval = (int)obj;//再拆箱

只有装过箱的数据才能拆箱