美文网首页
基本类型(一)

基本类型(一)

作者: 沈_ac89 | 来源:发表于2019-03-10 14:32 被阅读0次

    基本类型

    字符、字符串和文本处理

    字符

    基本概念

    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允许将数组元素从一种类型转型为另一种。

    • 前提是数组维数相同,而且必须存在从元素源类型到目标类型的隐式转换或者显式转换。

    • CLR不允许将值类型转换成其他任何类型。

      // 测试类型转换
      FileStream[,] fs = new FileStream[5, 10];
      // 隐式转换
      object[,] obj = fs;// 支持向上转型,协变性
      // 转型失败一:维度不同,不允许转型
      // Stream[] fs2 = obj;
      // 显示转型,成功
      Stream[,] fs2 = (Stream[,])obj;// 逆变性
      // 转型失败二:编译没有问题,运行有问题,类型不匹配
      // string[,] arrStr = (string[,])obj;
      // 值类型测试
      Int32[] arrInt = new int[10];
      // 转型失败三:值类型不允许转换成引用类型
      // object[] arrObject = arrInt;
      // 转型失败四:值类型不允许转换成其他值类型
      //Double[] arrDouble = arrInt;
      

    数组内部工作原理

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

    数组的内部工作原理

    • CLR支持两种不同的数组

      • 下限为0的一维数组,也称SZ(single-dimensional,zero-based)数组或者向量(Vector)。
      • 下限未知的一维或多维数组。
    • 演示二维数组的三种方式(安全、交错和不安全)

      using System;
      
      namespace ILLearning
      {
          public class ArrayDemoTest
          {
              private static int c_numElements = 10000;
      
              public static void Main()
              {
                  Int32[,] a2Dim = new Int32[c_numElements, c_numElements];
      
                  // 声明交错数组(向量构成的向量)
                  int[][] aJagged = new Int32[c_numElements][];
                  for (int i = 0; i <c_numElements; i++)
                  {
                      aJagged[i] = new Int32[c_numElements];
                  }
                  Safe2DimArrayAccess(a2Dim);
                  SafeJaggedArrayAccess(aJagged);
                  Unsafe2DimArrayAccess(a2Dim);
              }
      
              /// <summary>
              /// CLR安全方式访问
              /// </summary>
              /// <param name="a"></param>
              /// <returns></returns>
              private static Int32 Safe2DimArrayAccess(Int32[,] a)
              {
                  Int32 sum = 0;
                  for (int i = 0; i < c_numElements; i++)
                  {
                      for (int j = 0; j < c_numElements; j++)
                      {
                          sum += a[i, j];
                      }
                  }
                  return sum;
              }
      
              /// <summary>
              /// 交错访问
              /// </summary>
              /// <param name="a"></param>
              /// <returns></returns>
              private static Int32 SafeJaggedArrayAccess(Int32[][] a)
              {
                  Int32 sum = 0;
                  for (int i = 0; i < c_numElements; i++)
                  {
                      for (int j = 0; j < c_numElements; j++)
                      {
                          sum += a[i][j];
                      }
                  }
                  return sum;
              }
      
              private static unsafe Int32 Unsafe2DimArrayAccess(Int32[,] a)
              {
                  Int32 sum = 0;
                  // fixed的作用:
                  /*
                   fixed 语句可防止垃圾回收器重新定位可移动的变量。
                   fixed 语句仅允许存在于不安全的上下文中。
                   fixed 还可用于创建固定大小的缓冲区。
                   */
                  fixed (Int32* pi = a)// 声明指针pi,指向CLR类型a
                  {
                      for (Int32 i = 0; i < c_numElements; i++)
                      {
                          Int32 baseofDim = i * c_numElements;
                          for (int j = 0; j < c_numElements; j++)
                          {
                              sum += pi[baseofDim+j];
                          }
                      }
                  }
                  return sum;
              }
          }
      }
      
      • 待分析unsafe代码IL代码

        .method private hidebysig static int32 
                  Unsafe2DimArrayAccess(int32[0...,0...] a) cil managed
          {
            // 代码大小       118 (0x76)
            .maxstack  4
            .locals init (int32 V_0,
                     int32* V_1,
                     int32[0...,0...] pinned V_2,
                     int32 V_3,
                     int32 V_4,
                     int32 V_5,
                     bool V_6,
                     bool V_7,
                     int32 V_8)
            IL_0000:  nop
            IL_0001:  ldc.i4.0
            IL_0002:  stloc.0
            IL_0003:  ldarg.0
            IL_0004:  dup
            IL_0005:  stloc.2
            IL_0006:  brfalse.s  IL_0010
        
            IL_0008:  ldloc.2
            IL_0009:  callvirt   instance int32 [mscorlib]System.Array::get_Length()
            IL_000e:  brtrue.s   IL_0015
        
            IL_0010:  ldc.i4.0
            IL_0011:  conv.u
            IL_0012:  stloc.1
            IL_0013:  br.s       IL_001f
        
            IL_0015:  ldloc.2
            IL_0016:  ldc.i4.0
            IL_0017:  ldc.i4.0
            IL_0018:  call       instance int32& int32[0...,0...]::Address(int32,
                                                                           int32)
            IL_001d:  conv.u
            IL_001e:  stloc.1
            IL_001f:  nop
            IL_0020:  ldc.i4.0
            IL_0021:  stloc.3
            IL_0022:  br.s       IL_005d
        
            IL_0024:  nop
            IL_0025:  ldloc.3
            IL_0026:  ldsfld     int32 ILLearning.ArrayDemoTest::c_numElements
            IL_002b:  mul
            IL_002c:  stloc.s    V_4
            IL_002e:  ldc.i4.0
            IL_002f:  stloc.s    V_5
            IL_0031:  br.s       IL_0049
        
            IL_0033:  nop
            IL_0034:  ldloc.0
            IL_0035:  ldloc.1
            IL_0036:  ldloc.s    V_4
            IL_0038:  ldloc.s    V_5
            IL_003a:  add
            IL_003b:  conv.i
            IL_003c:  ldc.i4.4
            IL_003d:  mul
            IL_003e:  add
            IL_003f:  ldind.i4
            IL_0040:  add
            IL_0041:  stloc.0
            IL_0042:  nop
            IL_0043:  ldloc.s    V_5
            IL_0045:  ldc.i4.1
            IL_0046:  add
            IL_0047:  stloc.s    V_5
            IL_0049:  ldloc.s    V_5
            IL_004b:  ldsfld     int32 ILLearning.ArrayDemoTest::c_numElements
            IL_0050:  clt
            IL_0052:  stloc.s    V_6
            IL_0054:  ldloc.s    V_6
            IL_0056:  brtrue.s   IL_0033
        
            IL_0058:  nop
            IL_0059:  ldloc.3
            IL_005a:  ldc.i4.1
            IL_005b:  add
            IL_005c:  stloc.3
            IL_005d:  ldloc.3
            IL_005e:  ldsfld     int32 ILLearning.ArrayDemoTest::c_numElements
            IL_0063:  clt
            IL_0065:  stloc.s    V_7
            IL_0067:  ldloc.s    V_7
            IL_0069:  brtrue.s   IL_0024
        
            IL_006b:  nop
            IL_006c:  ldnull
            IL_006d:  stloc.2
            IL_006e:  ldloc.0
            IL_006f:  stloc.s    V_8
            IL_0071:  br.s       IL_0073
        
            IL_0073:  ldloc.s    V_8
            IL_0075:  ret
          } // end of method ArrayDemoTest::Unsafe2DimArrayAccess
        

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

    其他主题

    数组的传递和返回

    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";
          }
      }
      

    创建下限非零的数组

    • 演示案例

      // 目前比较少用
      public sealed class DynamicArrays
      {
          public static void Run()
          {
              Int32[] lowerBounds = { 2005, 1 };
              Int32[] lengths = { 5, 4 };
              Decimal[,] quarterlyRevernue = (Decimal[,])Array.CreateInstance(typeof(Decimal), lengths, lowerBounds);
              Console.WriteLine("{0,4} {1,9} {2,9} {3,9} {4,9}", "Year","Q1", "Q2", "Q3", "Q4");
              Int32 firstYear = quarterlyRevernue.GetLowerBound(0);
              Int32 lastYear = quarterlyRevernue.GetUpperBound(0);
              int firstQuarter = quarterlyRevernue.GetLowerBound(1);
              int lastQuarter = quarterlyRevernue.GetUpperBound(1);
      
              for(Int32 year = firstYear;year <= lastYear; year++)
              {
                  Console.Write(year + " ");
                  for (int quarter = firstQuarter; quarter <= lastQuarter; quarter++)
                  {
                      Console.Write("{0,9:C}", quarterlyRevernue[year, quarter]);
                  }
                  Console.WriteLine();
              }
          }
      }
      

    可空值类型

    基本概念

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

    相关文章

      网友评论

          本文标题:基本类型(一)

          本文链接:https://www.haomeiwen.com/subject/gsfepqtx.html