基本类型(一)

2019-03-10  本文已影响0人  沈_ac89

基本类型

字符、字符串和文本处理

字符

基本概念

  1. 基本结构

    • .Net Framework中,字符总是表示成16位的Unicode代码值。
    • 每个字符都是System.Char的实例,注意,该类型为值类型。
    • Char.MaxValue='/uffff',表示65535。
  2. Culture(字符或者字符串有文化的差异,需要注意)

  3. 转换(效率由高到低)

    • 转型(强制类型转换)

      Char a = (Int32)65;// A 
      
    • 使用Convert类型

      Char a = Convert.ToChar(65); // A
      
    • 使用IConvertible接口

      Char a = (IConvertible(65)).ToChar(null); // A
      
      
  4. 思考这两种写法有什么不同?

    // 思考:这两种写法有什么不同?
    char b = unchecked((Char)(65536*2 + 65));
    Console.WriteLine(b);
    Char d = unchecked(Convert.ToChar(70000));// 这种是编译不过的
    Console.WriteLine(d);
    
    • IL分析

      // 有效的IL代码
      .entrypoint
      // 代码大小       30 (0x1e)
      .maxstack  1
      .locals init (char V_0,
               char V_1)
      IL_0000:  nop
      IL_0001:  ldc.i4.s   65 // 实际强制转换会减去65536的整数倍后再进行转换。
      IL_0003:  stloc.0
      IL_0004:  ldloc.0
      IL_0005:  call       void [mscorlib]System.Console::WriteLine(char)
      IL_000a:  nop
      IL_000b:  ldc.i4     0x11170 // 已经超过Char.MaxValue
      IL_0010:  call       char [mscorlib]System.Convert::ToChar(int32)
      IL_0015:  stloc.1
      IL_0016:  ldloc.1
      IL_0017:  call       void [mscorlib]System.Console::WriteLine(char)
      IL_001c:  nop
      IL_001d:  ret
      

参考内容

  1. 字符集介绍
  2. UniCode码介绍
  3. UniCode编码表

字符串

String

基本概念
  1. 基本结构

    • string表示一个不可变的顺序字符集(immutable)。
    • String对象(它的字符数组)总是存在于堆上。
      • 特别说明一点,struct上面的string也不例外,若是局部变量,在Stack上,若是引用类型的成员,则在Heap上。但是注意的是,当struct拷贝一个副本的时候,引用的堆控件也会重新分配初始化
      • 关于struct的说明,可以参考MSDN-Struct
    • string不允许使用new关键字构造string对象。
    • 字符串换行建议使用Environment.NewLine,而不是/r/n。
    • 编程技巧:要在序号比较前更改字符串中的字符的大小写,应该使用String.ToUpperInvariant进行正规化(normalizing),微软对执行大写比较代码进行过优化。
  2. 语言文化(Culture)

    • 后续补上,目前用到不多
  3. 字符串留用(string interning)

    • 原理

      1. 原理说明
         CLR初始化的时候创建一个内部哈希表,在这个表中,键(key)是字符串,而值(value)是对托管堆中的string对象的引用,开始时候,哈希表为空。
         暴露的API:
         - public static String Intern(String str);
         - public static String IsInterned(String str);
      2. 影响CLR的特性
         - CompilationRelaxationAttribute
      
      
  4. 字符串池

高效处理字符串

  1. StringBuilder
  2. ToString
    • IFormatProvider
  3. Parse

安全字符串

  1. 后续整理

文本处理

编码(后续整理)

  1. UTF-16(Unicode编码)

  2. UTF-8

    // 简单案例
    public class EncodingTest
    {
        public static void Run()
        {
            string str = "hello,world";
            // 获取utf8
            var utf8Encoding = Encoding.UTF8;
            // 将字符串编译成字符串数组
            var encodingBytes = utf8Encoding.GetBytes(str);
            // 显示编码好的值
            Console.WriteLine(BitConverter.ToString(encodingBytes));
            // 解码
            var str2 = utf8Encoding.GetString(encodingBytes);
            Console.WriteLine(str2);
            Console.WriteLine("是否相等:" + Object.ReferenceEquals(str, str2));
        }
    }
    

枚举类型和位标志

基本概念

  1. 枚举是值类型

  2. 使用枚举的理由

    • 枚举类型使程序更容易编写、阅读和维护。
    • 枚举是强类型的,不容易写错。
  3. 常见的用法

    public class EnumTest
    {
        public static void Run()
        {
            // 强制转换
            ColorEnum color = (ColorEnum)1; // Pink
            if(color == ColorEnum.Pink)
            {
                Console.WriteLine("强制转换成功");
            }
            // 数字转换
            int num = (int)color; // 1
            Console.WriteLine("转换成数字:" + num); 
            // 获取枚举项的名称
            string name = Enum.GetName(typeof(ColorEnum), 1); // Pink
            Console.WriteLine("查找到的枚举值为1的名称:" + name);
        }
    }
    
    public enum ColorEnum
    {
        Orange,
        Pink
    }
    

位标志

  1. 普通枚举和位标志的区别

    • 位标志为可以用来表示一组可以组合的枚举类型。
    • 位标志表示位集合。
    • 枚举值可以从0开始,按2的n-1次方表示,但是不一定是2的n次方。
    • 可以用[Flags]特性标注位标志。
  2. 案例分析

    public class Test
    {
        public static void Main(string[] args)
        {
            // 位标志
            MyFlagEnum actions = MyFlagEnum.Close | MyFlagEnum.Open;// 增加,等于十进制的3,并装箱到actions指向的Heap中MyFlagEnum
            Console.WriteLine(actions.ToString("F")); // Close,Open
            MyFlagEnum myFlag = (MyFlagEnum)Enum.Parse(typeof(MyFlagEnum), "open", true);
            Console.WriteLine(myFlag.ToString("F"));// Open
            myFlag = (MyFlagEnum)Enum.Parse(typeof(MyFlagEnum), "2", false);
            Console.WriteLine(myFlag.ToString("F"));// Close
            myFlag = (MyFlagEnum)Enum.Parse(typeof(MyFlagEnum), "3", false);
            Console.WriteLine(myFlag.ToString("F")); // Open,Close
            myFlag = MyFlagEnum.All | MyFlagEnum.None; // 为什么这里只会出现ALL,从最大值计算减掉当前值,向下找组合,但是0不会给找到。
            Console.WriteLine(myFlag.ToString("F"));// ALL
        }
    }
    
    [Flags]
    public enum MyFlagEnum
    {
        None = 0,
        Open = 0x0001,
        Close = 0x0002,
        All = 0x001F //十进制31
    }
    
    • IL代码分析

      .class public auto ansi beforefieldinit ILLearning.Test
             extends [mscorlib]System.Object
      {
        .method public hidebysig static void  Main(string[] args) cil managed
        {
          .entrypoint
          // 代码大小       198 (0xc6)
          .maxstack  3
          .locals init (valuetype ILLearning.MyFlagEnum V_0,
                   valuetype ILLearning.MyFlagEnum V_1)
          IL_0000:  nop
          IL_0001:  ldc.i4.3
          IL_0002:  stloc.0
          IL_0003:  ldloc.0
          IL_0004:  box        ILLearning.MyFlagEnum
          IL_0009:  ldstr      "F"
          IL_000e:  call       instance string [mscorlib]System.Enum::ToString(string)
          IL_0013:  call       void [mscorlib]System.Console::WriteLine(string)
          IL_0018:  nop
          IL_0019:  ldtoken    ILLearning.MyFlagEnum
          IL_001e:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
          IL_0023:  ldstr      "open"
          IL_0028:  ldc.i4.1  // true,在IL中表示1,false表示0
          IL_0029:  call       object [mscorlib]System.Enum::Parse(class [mscorlib]System.Type,
                                                                   string,
                                                                   bool)
          IL_002e:  unbox.any  ILLearning.MyFlagEnum
          IL_0033:  stloc.1
          IL_0034:  ldloc.1
          IL_0035:  box        ILLearning.MyFlagEnum
          IL_003a:  ldstr      "F"
          IL_003f:  call       instance string [mscorlib]System.Enum::ToString(string)
          IL_0044:  call       void [mscorlib]System.Console::WriteLine(string)
          IL_0049:  nop
          IL_004a:  ldtoken    ILLearning.MyFlagEnum
          IL_004f:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
          IL_0054:  ldstr      "2"
          IL_0059:  ldc.i4.0
          IL_005a:  call       object [mscorlib]System.Enum::Parse(class [mscorlib]System.Type,
                                                                   string,
                                                                   bool)
          IL_005f:  unbox.any  ILLearning.MyFlagEnum
          IL_0064:  stloc.1
          IL_0065:  ldloc.1
          IL_0066:  box        ILLearning.MyFlagEnum
          IL_006b:  ldstr      "F"
          IL_0070:  call       instance string [mscorlib]System.Enum::ToString(string)
          IL_0075:  call       void [mscorlib]System.Console::WriteLine(string)
          IL_007a:  nop
          IL_007b:  ldtoken    ILLearning.MyFlagEnum
          IL_0080:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
          IL_0085:  ldstr      "3"
          IL_008a:  ldc.i4.0
          IL_008b:  call       object [mscorlib]System.Enum::Parse(class [mscorlib]System.Type,
                                                                   string,
                                                                   bool)
          IL_0090:  unbox.any  ILLearning.MyFlagEnum
          IL_0095:  stloc.1
          IL_0096:  ldloc.1
          IL_0097:  box        ILLearning.MyFlagEnum
          IL_009c:  ldstr      "F"
          IL_00a1:  call       instance string [mscorlib]System.Enum::ToString(string)
          IL_00a6:  call       void [mscorlib]System.Console::WriteLine(string)
          IL_00ab:  nop
          IL_00ac:  ldc.i4.s   31
          IL_00ae:  stloc.1
          IL_00af:  ldloc.1
          IL_00b0:  box        ILLearning.MyFlagEnum
          IL_00b5:  ldstr      "F"
          IL_00ba:  call       instance string [mscorlib]System.Enum::ToString(string)
          IL_00bf:  call       void [mscorlib]System.Console::WriteLine(string)
          IL_00c4:  nop
          IL_00c5:  ret
        } // end of method Test::Main
      
        .method public hidebysig specialname rtspecialname 
                instance void  .ctor() cil managed
        {
          // 代码大小       8 (0x8)
          .maxstack  8
          IL_0000:  ldarg.0
          IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
          IL_0006:  nop
          IL_0007:  ret
        } // end of method Test::.ctor
      
      } // end of class ILLearning.Test
      
      .class public auto ansi sealed ILLearning.MyFlagEnum
             extends [mscorlib]System.Enum
      {
        .custom instance void [mscorlib]System.FlagsAttribute::.ctor() = ( 01 00 00 00 ) 
        .field public specialname rtspecialname int32 value__
        .field public static literal valuetype ILLearning.MyFlagEnum None = int32(0x00000000)
        .field public static literal valuetype ILLearning.MyFlagEnum Open = int32(0x00000001)
        .field public static literal valuetype ILLearning.MyFlagEnum Close = int32(0x00000002)
        .field public static literal valuetype ILLearning.MyFlagEnum All = int32(0x0000001F)
      } // end of class ILLearning.MyFlagEnum
      

数组

基本概念

  1. 数组为引用类型,是在托管堆中分配的。
  2. 数组隐式继承自System.Array

数组转型

对于元素类型为引用类型的数组,CLR允许将数组元素从一种类型转型为另一种。

数组内部工作原理

  1. 隐式派生自System.Array
  2. 所有数组隐式实现IEnumerable、ICollection和IList。

数组的内部工作原理

不安全的数组(后续补充)

其他主题

数组的传递和返回

  1. 数组作为实参传递给方法时候,实际传递的是数组的引用。

  2. Array.Copy方法执行的是浅拷贝,浅拷贝怎么理解?

    public class Test
    {
        public static void Main(string[] args)
        {
            // Array.Copy
            string[] hhs = new string[3];
            hhs[0] = "hi";
            hhs[1] = ",";
            string[] hhs2 = new string[5];
            hhs.CopyTo(hhs2,0);// 实际上,这里的效果是深复制,应该是两次New都有在堆中分配了内存空间,不会再单独开辟栈引用,指向其他的内存空间
        }
    }
    
    • 主要IL代码分析

      .module test.exe
      // MVID: {B90E3704-F778-4BDE-8F93-5D1D1296068D}
      .imagebase 0x00400000
      .file alignment 0x00000200
      .stackreserve 0x00100000
      .subsystem 0x0003       // WINDOWS_CUI
      .corflags 0x00000001    //  ILONLY
      // Image base: 0x06870000
      
      
      // =============== CLASS MEMBERS DECLARATION ===================
      
      .class public auto ansi beforefieldinit ILLearning.Test
             extends [mscorlib]System.Object
      {
        .method public hidebysig static void  Main(string[] args) cil managed
        {
          .entrypoint
          // 代码大小       41 (0x29)
          .maxstack  3
          .locals init (string[] V_0,
                   string[] V_1)
          IL_0000:  nop
          IL_0001:  ldc.i4.3
          IL_0002:  newarr     [mscorlib]System.String // 创建数组hhs
          IL_0007:  stloc.0
          IL_0008:  ldloc.0
          IL_0009:  ldc.i4.0
          IL_000a:  ldstr      "hi"
          IL_000f:  stelem.ref // 用计算堆栈上的对象 ref 值(O 类型)替换给定索引处的数组元素。
          IL_0010:  ldloc.0
          IL_0011:  ldc.i4.1
          IL_0012:  ldstr      ","
          IL_0017:  stelem.ref
          IL_0018:  ldc.i4.5
          IL_0019:  newarr     [mscorlib]System.String
          IL_001e:  stloc.1
          IL_001f:  ldloc.0
          IL_0020:  ldloc.1
          IL_0021:  ldc.i4.0
          IL_0022:  callvirt   instance void [mscorlib]System.Array::CopyTo(class [mscorlib]System.Array,
                                                                            int32)
          IL_0027:  nop
          IL_0028:  ret
        } // end of method Test::Main
      
  3. 数组的实参传递

    // 测试数组传递的是引用
    public static void Test()
    {           
     string[] hhs = new string[3];
        hhs[0] = "hi";
        hhs[1] = ",";
        foreach (var item in hhs)
        {
            Console.WriteLine(item);// hhs[0] = hi
        }
        SayHi(hhs);
        foreach (var item in hhs)
        {
            Console.WriteLine(item);// hhs[0] = "HelloWorld"
        }
    }
    
    public static void SayHi(string[] hehes)
    {
        if (hehes != null && hehes.Count() > 1)
        {
            hehes[0] = "HelloWorld";
        }
    }
    

创建下限非零的数组

可空值类型

基本概念

  1. System.Nullable<T>仍然为值类型
  2. ??可空符号优化
上一篇下一篇

猜你喜欢

热点阅读