c#数据类型和类型转换

C# 数据类型

C# 中,变量分为以下几种类型:

  • 值类型(Value types
  • 引用类型(Reference types
  • 指针类型(Pointer types

值类型(Value types

值类型变量可以直接分配给一个值。它们是从类 System.ValueType 中派生的。

值类型直接包含数据。比如 intcharfloat,它们分别存储数字、字符、浮点数。当您声明一个 int 类型时,系统分配内存来存储值。

下表列出了 C# 2010 中可用的值类型:

 

 

如需得到一个类型或一个变量在特定平台上的准确尺寸,可以使用 sizeof 方法。表达式 sizeof(type) 产生以字节为单位存储对象或类型的存储尺寸。下面举例获取任何机器上 int 类型的存储尺寸:

using System;

 

namespace DataTypeApplication

{

   class Program

   {

      static void Main(string[] args)

      {

         Console.WriteLine("Size of int: {0}", sizeof(int));

         Console.ReadLine();

      }

   }

}

当上面的代码被编译和执行时,它会产生下列结果:

Size of int: 4

引用类型(Reference types

引用类型不包含存储在变量中的实际数据,但它们包含对变量的引用。

换句话说,它们指的是一个内存位置。使用多个变量时,引用类型可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的 引用类型有:objectdynamic  string

对象(Object)类型

对象(Object)类型 C# 通用类型系统(Common Type System - CTS)中所有数据类型的终极基类。Object System.Object 类的别名。所以对象(Object)类型可以被分配任何其他类型(值类型、引用类型、预定义类型或用户自定义类型)的值。但是,在分配值之前,需要先进行类型转换。

当一个值类型转换为对象类型时,则被称为 装箱;另一方面,当一个对象类型转换为值类型时,则被称为 拆箱

object obj;

obj = 100; // 这是装箱

动态(Dynamic)类型

您可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。

声明动态类型的语法:

dynamic <variable_name> = value;

例如:

dynamic d = 20;

动态类型与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的。

字符串(String)类型

字符串(String)类型 允许您给变量分配任何字符串值。字符串(String)类型是 System.String 类的别名。它是从对象(Object)类型派生的。字符串(String)类型的值可以通过两种形式进行分配:引号和 @引号。

例如:

String str = "csharp.com";

一个 @引号字符串:

@"csharp.com";

C# string 字符串的前面可以加 @(称作"逐字字符串")将转义字符(\)当作普通字符对待,比如:

string str = @"C:\Windows";

等价于:

string str = "C:\\Windows";

@ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。

string str = @"<script type=""text/javascript"">

    <!--

    -->

</script>";

用户自定义引用类型有:classinterface delegate

指针类型(Pointer types

指针类型变量存储另一种类型的内存地址。C# 中的指针与 C C++ 中的指针有相同的功能。

声明指针类型的语法:

type* identifier;

例如:

char* cptr;

int* iptr;

补充1

关于装箱和拆箱

就像仓库,仓库里有货架,货架上有编号:A1,A2,A3...................., 这些编号就可以看做是引用类型,现在来了一批货,有 土豆,黄瓜,西红柿,这些就是值类型,如果你想让 A1=土豆,那么就要把土豆搬到 A1 里面去,这就叫装箱,装箱需要耗费人力和工时(也就是耗费CPU和内存),同理拆箱就要把对应编号的货物搬出来,也是需要耗费人力和工时。

装箱:值类型转换为对象类型, 实例:

int val = 8;

object obj = val;//整型数据转换为了对象类型(装箱)

拆箱:之前由值类型转换而来的对象类型再转回值类型, 实例:

int val = 8;

object obj = val;//先装箱

int nval = intobj;//再拆箱

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

 

补充2:

obj int之间关系

using System;

namespace RectangleApplication

{

    class ExecuteRectangle

    {

        static void Main(string[] args)

        {

            int a=9;

            object obj;

            obj = a;

            obj =10;

            Console.WriteLine("2: {0}", a);   // 输出:2: 9

            Console.WriteLine("1: {0}", obj); // 输出:1: 10

            Console.ReadLine();

        }

    }

} 

 

设置值 int a=9; obj=a; obj改变不会对 int a 进行改变,object 只是复制了 int a 的值出来然后对其操作而已。不会影响到 int 原来的值。

补充3

C# String string 的区别

string C# 中的类,String .net Framework 的类(C# IDE 中不会显示蓝色) C# string 映射为 .net Framework String 如果用 string, 编译器会把它编译成 String,所以如果直接用 String 就可以让编译器少做一点点工作。

如果使用 C#,建议使用 string,比较符合规范 string 始终代表 System.String(1.x) ::System.String(2.0) String 只有在前面有 using System;的时候并且当前命名空间中没有名为 String 的类型(classstructdelegateenum)的时候才代表 System.String string 是关键字,String 不是,也就是说 string 不能作为类、结构、枚举、字段、变量、方法、属性的名称,而 String 可以。

String CLR 的类型名称(也算是关键字),而 string C# 中的关键字。string 在编译时候 C# 编译器会默认将其转换为 String,在这里会多增加几行转换的代码。很多时候都是建议使用 CLR 的类型而不要使用 C# 的类型(这是专家的建议)。比如说还有:使用 int 的时候最好使用 Int32 等。很多时候都是一个习惯问题,规范问题。还有一个不同就是在 VS 中表现的颜色不一样:String 是绿色,string 是蓝色。

补充4

关于值类型、引用类型以及的关系

值类型,声明一个值类型的时候,是在中开辟一个内存空间来存放对应的值,当值类型的值发生改变的时候,则直接修改该内存空间所保存的值。例:

int n1 = 5;

int n2 = n1;

Console.WriteLine(n1 + "  "+ n2);    // 5  5

n2 = 7;

Console.WriteLine(n1 + "  " + n2)    // 5  7

 

这里首先在中开辟一个内存空间用来保存 n1 的值 5,接着再在中开辟一个新的内存空间用来保存 n2 的值 5,所以显示出来的结果是 5 5。然后将 n2 中对应的内存空间保存的值修改成 7,故显示出来的结果是 5 7

引用类型,声明一个引用类型的时候,首先是在中开辟一个内存空间来存放对应的值,然后在中开辟一个内存空间用于保存在中开辟的内存空间的地址。当系统调用引用类型的时候,首先去中获取到地址,然后根据地址在中找到对应的内存空间来获取到对应值。像数组这样的引用类型

string[] a1 = new string[]{ "a" , "b" , "c" };

string[] a2 = a1;

for(int i = 0; i < a2.Length; i++)

{

    Console.Write(a2[i] + " ");    //a b c

}

a1[2] = "d";

Console.WriteLine();            //换行

for(int i = 0; i < a2.Length; i++)

{

    Console.Write(a2[i] + " ");    //a b d

}

Console.WriteLine(); 

 

这里首先是在中开辟一个内存空间(假设:0X55)用来保存数组a1的值,然后在中开辟一个内存空间(a1)用于保存地址 0X55。当将 a1 赋给 a2 时,是将地址赋给 a2,即在中开辟一个内存空间(a2)用于保存地址 0X55,所以输出 a2 的值是 a b c。当将 a1[2]修改成”d”的时候,修改的是0X55 内存空间保存的值,因为 a2 的地址和 a1 的地址一样,所以输出结果是 a b d

string 是一个特殊的引用类型,先看下面代码:

string a = "123";

string b = a; 

Console.WriteLine(a+" "+b);  //123 123

string b = "456";

Console.WriteLine(a+" "+b);  //123 456

 

和数组类似的,这里首先在中开辟一个内存空间(假设:0X88)用来保存 a 的值 123,然后在中开辟一个内存空间(a)用于保存地址 0X88

和数组不同的是,当将 a 赋给 b 的时候,首先是在中开辟一个新的内存空间(假设:0X101)用于保存值 123,然后在中开辟一个内存空间(b)用于保存地址 0X101,所以输出的结果是 123 123

当修改 b 值时,并不是修改0X101 内存空间的值,而是在中重新开辟一个新的内存空间(假设:0X210)用于保存 b 修改后的值,然后将 b 中对应的内存空间的所保存的地址修改成 0X210,所以输出的结果是 123 456。而中的 0X101 内存空间将在下次的垃圾回收中被回收利用。

 

C# 类型转换

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

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

下面的实例显示了一个显式的类型转换:

实例

namespace TypeConversionApplication
{
    class ExplicitConversion
    {
        static void Main(string[] args)
        {
            double d = 5673.74;
            int i;

            // 强制转换 double 为 int
            i = (int)d;
            Console.WriteLine(i);
            Console.ReadKey();
            
        }
    }
}

 

当上面的代码被编译和执行时,它会产生下列结果:

5673

C# 类型转换方法

C# 提供了下列内置的类型转换方法:

序号

方法 & 描述

1

ToBoolean
如果可能的话,把类型转换为布尔型。

2

ToByte
把类型转换为字节类型。

3

ToChar
如果可能的话,把类型转换为单个 Unicode 字符类型。

4

ToDateTime
把类型(整数或字符串类型)转换为 日期-时间 结构。

5

ToDecimal
把浮点型或整数类型转换为十进制类型。

6

ToDouble
把类型转换为双精度浮点型。

7

ToInt16
把类型转换为 16 位整数类型。

8

ToInt32
把类型转换为 32 位整数类型。

9

ToInt64
把类型转换为 64 位整数类型。

10

ToSbyte
把类型转换为有符号字节类型。

11

ToSingle
把类型转换为小浮点数类型。

12

ToString
把类型转换为字符串类型。

13

ToType
把类型转换为指定类型。

14

ToUInt16
把类型转换为 16 位无符号整数类型。

15

ToUInt32
把类型转换为 32 位无符号整数类型。

16

ToUInt64
把类型转换为 64 位无符号整数类型。

下面的实例把不同值的类型转换为字符串类型:

实例

namespace TypeConversionApplication
{
    class StringConversion
    {
        static void Main(string[] args)
        {
            int i = 75;
            float f = 53.005f;
            double d = 2345.7652;
            bool b = true;

            Console.WriteLine(i.ToString());
            Console.WriteLine(f.ToString());
            Console.WriteLine(d.ToString());
            Console.WriteLine(b.ToString());
            Console.ReadKey();
            
        }
    }
}

 

当上面的代码被编译和执行时,它会产生下列结果:

75

53.005

2345.7652

True

补充1

隐式转换和显式转换

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

实例:

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

        }

    }

}

 

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

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

实例:

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);

 

运行结果:

FALSE

FALSE

is操作符:检查对象是否与给定类型兼容。

as运算符:用于在兼容的引用类型之间执行转换

as操作符类似于强制转换,但又有区别,当对象为null时,不会抛异常而是会返回null

补充2

类型之间的转换 - Convert Parse

string locstr = 123.ToString();

 

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

 

//方法一: Convert

int i = Convert.ToInt16(locstr);

 

 

//方法二: Parse

int ii = int.Parse(locstr);

int.TryParse(string s,out int i)

 

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

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);

    }

}

 

结果输出:

abcd False 0

1234 True 1234

补充3

C# 中对 double 类型的数据取整,可以使用 convert.toint32() 方法,也可使用 int 强制转换为整数,使用 int 时并不存在四舍五入的情况,而是直接将后面的小数位数丢掉。比如:

class Program

{

    static void Main(string[] args)

    {

        double a = 1.35;

        double b = 1.65;

        int a1 = Convert.ToInt32(a);

        int a2 = (int)(a);

        int b1 = Convert.ToInt32(b);

        int b2 = (int)(b);

        Console.WriteLine("{0}使用convert方法转化的结果为:{1}",a,a1);

        Console.WriteLine("{0}使用int强制转换的结果为:{1}",a,a2);

        Console.WriteLine("{0}使用convert方法转化的结果为:{1}", b, b1);

        Console.WriteLine("{0}使用int强制转换的结果为:{1}", b, b2);

        Console.ReadKey();

    }

}

 

程序运行结果如下:

1.35使用convert方法转化的结果为:1

1.35使用int强制转换的结果为:1

1.65使用convert方法转化的结果为:2

1.65使用int强制转换的结果为:1

补充4

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

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

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

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

  • a. Convert.ToInt32(double value) 如果 value 为两个整数中间的数字,则返回二者中的偶数;即 3.5 转换为 44.5 转换为 4,而 5.5 转换为 6。不过 4.6 可以转换为 54.4 转换为 4
  • b. int.Parse("4.5") 直接报错:"输入字符串的格式不正确"

(3) 对被转换类型的区别 int.Parse 是转换 String int, Convert.ToInt32 是转换继承自 Object 的对象为 int (可以有很多其它类型的数据)。你得到一个 object 对象, 你想把它转换为 int,  int.Parse 就不可以, 要用 Convert.ToInt32

posted @ 2019-05-27 10:07  宅宅小朋友  阅读(6583)  评论(0编辑  收藏  举报