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

  Hi,小伙伴们,我们又见面了,这个鼠年大家过得怎么样啊,想必也是被这疫情搞得很闹心吧,都在家憋坏了吧。难得有这么长的假期,不如利用这段时间多学习学习。在家好好待着就是为国家做贡献了,在此,为那些奋战在疫情一线的医护人员致以崇高的敬意,也希望大家都平平安安!好了,下面言归正传。

  上一节,我们通过一个HelloWorld程序介绍了C#程序的结构以及如何编译运行一个C#程序。本节我们将学习C#语言中的数据类型。

  C#有两种类型:值类型和引用类型。值类型的变量包含类型的实例也就是直接包含数据,值类型变量分配在托管栈中,会在创建它们的方法返回时自动释放。而引用类型的变量是包含对存储数据(称为“对象”)的引用,数据分配在托管堆中,变量并不会在创建它们的方法结束时释放内存,它们所占用的内存会被CLR中的GC(垃圾回收机制)释放。对于引用类型,两个变量可以引用同一对象;因此,对一个变量执行的运算可能会影响另一个变量引用的对象。 而值类型,每个变量都有自己的数据副本;因此,对一个变量执行的运算不会影响另一个变量(ref 和 out 参数变量除外)上代码

 1 //Struct是值类型
 2     public struct Location
 3     {
 4         public int X;
 5         public int Y;
 6         public Location(int x, int y)
 7         {
 8             X = x;
 9             Y = y;
10         }
11 
12         public override string ToString() => $"X:{X};Y:{Y}";
13 
14     }
15 
16     //Class是引用类型
17     public class Student
18     {
19         public int Age;
20         public string Name;
21         public Student(int age, string name) => (Age, Name) = (age, name);
22 
23         public override string ToString() => $"姓名:{Name};年龄:{Age}";
24 
25     }
26    public class Program
27     {
28         static void Main(string[] args)
29         {
30             //值类型示例
31 
32             Console.WriteLine("========值类型示例===========");
33             var location1 = new Location(1, 2);
34 
35             var location2 = location1;
36             location2.Y = 10;
37 
38             Console.WriteLine($"{nameof(location1)} 在 {nameof(location2)} 修改后值没有发生变化:{location1}");
39             Console.WriteLine($"{nameof(location2)}:{location2}");
40 
41             //引用类型示例
42             Console.WriteLine("========引用类型示例===========");
43             var student1 = new Student(12,"小明");
44             var student2 = student1;
45             student2.Age = 18;
46 
47             Console.WriteLine($"{nameof(student1)} 在 {nameof(student2)} 修改后值发生变化:{student1}");
48             Console.WriteLine($"{nameof(student2)}:{student2}");
49             Console.ReadKey();
50 
51             
52         }
53         
54     }

  在上面的代码中定义了一个值类型数据Location,一个引用类型数据Student。在Main方法中定义了location1,设置X,Y值,又定义了location2,并将location1赋值给location2,接着修改了location2.Y值,最后输出结果。然后又定义了student1,设置Age、Name值,又定义了student2,将student1赋值给student2,最输出结果。

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

 

   从程序运行结果中可以看出两个值类型变量location1和location2,对location2的操作并不会影响location1的数值,它们各自指向不同的内存位置。两个引用类型变量student1和student2,对student2的操作会影响student1的数值,这两个变量指向的是同一个内存位置。

  下图是今天要介绍C#的数据类型

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

  一、值类型

  整型数据类型

  整型数值类型表示整数。 所有的整型数值类型均为值类型,都支持算术、位逻辑、比较和相等运算符。每个整型类型都有MinValue和MaxValue常量,提供该类型的最小值和最大值。默认值都为0

 

C#类型/关键字

范围

大小

.NET类型

sbyte

-128到127

8 位带符号整数

System.SByte

byte

0到255

无符号8位整数

System.Byte

short

-32768到32767

有符号16位整数

System.Int16

ushort

0到65535

无符号16位整数

System.UInt16

int

-2,147,483,648 到 2,147,483,647

有符号32位整数

System.Int32

uint

0到4,294,967,295

无符号32位整数

System.UInt32

long

-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807

64 位带符号整数

System.Int64

ulong

0 到 18,446,744,073,709,551,615

无符号 64 位整数

System.UInt64

  在上表中,最左侧列中的每个C#类型关键字都是相应 .NET 类型的别名。

Int a = 123;
System.Int32 b = 456;

  以上两行代码声明两个类型相同的变量。

  浮点数据类型

  浮点数值类型表示实数。所有浮点数值类型都支持算术、比较和相等运算符。每个浮点类型都有 MinValue 和 MaxValue 常量,提供该类型的最小值和最大有限值。 默认值都为0。float and double 类型还提供可表示非数字和无穷大值的常量。例如,double 类型提供以下常量:Double.NaN、Double.NegativeInfinity 和 Double.PositiveInfinity。

与 float 和 double 相比,decimal 类型具有更高的精度和更小的范围,因此它适合于财务和货币计算。

C#类型/关键字

大致范围

大小

.NET类型

float

±1.5x10−45至±3.4 x 1038

4 个字节

System.Single

double

±5.0×10−324 至 ±1.7 × 10308

8 字节

System.Double

decimal

±1.0 x 10-28 至 ±7.9228 x 1028

16 个字节

System.Decimal

  布尔类型

  bool 类型关键字是 .NET System.Boolean 结构类型的别名,它表示一个布尔值,可为 true 或 false。默认值为false。

  Char

  char 类型关键字是 .NET System.Char 结构类型的别名,它表示 Unicode UTF-16 字符。默认值为0

类型

范围

大小

.NET类型

Char

U+0000到 U+FFFF

16 位

System.Char

  枚举类型

  枚举类型是由基础整型数值类型的一组命名常量定义的值类型,使用enum关键字定义枚举类型。

enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
}

  默认情况下,枚举成员的关联常数值为类型int;它们从零开始,并按定义文本顺序递增1。以上代码中Spring数值为0,Summer数值为1,Autumn数值为2,Winter数值为3。另外也可以显示指定关联的常数值。

Enum ErrorType
{
    None = 0,
    Unknow = 1,
    DisConnection = 100
}

  Struct结构

  Struct通常用来封装相关的变量组,例如下面的代码

public struct Book
{
    public decimal price;
    public string title;
    public string author;
    public int id;
}

  结构的特点

  • 结构可带有方法、字段、索引、属性、运算符方法和事件。
  • 结构可定义构造函数,但不能定义析构函数。但是,您不能为结构定义无参构造函数。无参构造函数(默认)是自动定义的,且不能被改变。
  • 与类不同,结构不能继承其他的结构或类。
  • 结构不能作为其他结构或类的基础结构。
  • 结构可实现一个或多个接口。
  • 结构成员不能指定为 abstract、virtual 或 protected。
  • 当您使用 New 操作符创建一个结构对象时,会调用适当的构造函数来创建结构。与类不同,结构可以不使用 New 操作符即可被实例化。
  • 如果不使用 New 操作符,只有在所有的字段都被初始化之后,字段才被赋值,对象才被使用。

  结构与类的区别

  • 类是引用类型,结构是值类型。
  • 结构不支持继承。
  • 结构不能声明默认的构造函数。
  • 结构体中声明的字段无法赋予初值,类可以。执行以下代码将出现“结构中不能实例属性或字段初始值设定”的报错
Public struct TestStruct
{
    Private int Value = 1;
}
  • 结构体的构造函数中,必须为结构体所有字段赋值,类的构造函数无此限制

  可为Null的值类型

  可为 null 值类型T? 表示其基础值类型 T 的所有值及额外的 null 值,例如,可以将以下三个值中的任意一个指定给 bool? 变量:true、false 或 null。

  二、引用类型

  对象类型

  object 类型是 System.Object 在 .NET 中的别名。 在 C# 的统一类型系统中,所有类型(预定义类型、用户定义类型、引用类型和值类型)都是直接或间接从 System.Object 继承的。 可以将任何类型的值赋给 object 类型的变量。 可以使用文本 null 将任何 object 变量赋值给其默认值。

  字符串类型

  string 类型表示零个或多个 Unicode 字符的序列。 string 是 System.String 在 .NET 中的别名。

  字符串是不可变的 ,即:字符串对象在创建后,尽管从语法上看似乎可以更改其内容,但事实上并不可行。 例如,编写此代码时,编译器实际上会创建一个新的字符串对象来保存新的字符序列,且该新对象将赋给 b。

string b = "h";
b += "ello";

“+”运算符连接字符串

String a = “Good”+ “Morning”;

[]运算符可用于只读访问字符串的个别字符。 有效索引于 0 开始,且必须小于字符串的长度:

String test = “test”;
Char x = test[0]; 

上述程序中x为”t”

  委托类型

  委托类型的声明与方法签名相似。 它有一个返回值和任意数目任意类型的参数。在 .NET 中,System.Action 和 System.Func 类型为许多常见委托提供泛型定义。delegate 是一种可用于封装命名方法或匿名方法的引用类型。 委托类似于 C++ 中的函数指针;但是,委托是类型安全和可靠的。通过将委托与命名方法或匿名方法关联,可以实例化委托。

  声明委托语法如下

delegate <return type> <delegate-name> <parameter list>

  下面委托代码可被用于引用任何一个带有一个单一的 string 参数的方法,并返回一个 int 类型变量

public delegate int MyDelegate (string s);

  实例化委托

  声明了委托类型,委托对象必须使用 new 关键字来创建,且与一个特定的方法有关。当创建委托时,传递到 new 语句的参数就像方法调用一样书写,但是不带有参数。上代码

  public delegate int NumberChange(int s);
    class Program
    {
        private static int Num = 100;
        public static int Add(int n) => Num += n;

        public static int Subtract(int n) => Num -= n;
        
        public static int GetResult()
        {
            return Num;
        }
        static void Main(string[] args)
        {
            NumberChange change1 = new NumberChange(Add);
            NumberChange change2 = new NumberChange(Subtract);

            change1(100);
            Console.WriteLine($"Value of Num:{GetResult()}");

            change2(5);
            Console.WriteLine($"Value of Num:{GetResult()}");

            Console.ReadKey();
        }
    }

  动态类型

  dynamic 类型表示变量的使用和对其成员的引用绕过编译时类型检查。 改为在运行时解析这些操作。 dynamic 类型简化了对 COM API、动态 API HTML 文档对象模型 (DOM) 的访问。

在大多数情况下,dynamic 类型与 object 类型的行为类似。 具体而言,任何非 Null 表达式都可以转换为 dynamic 类型。 dynamic 类型与 object 的不同之处在于,编译器不会对包含类型 dynamic 的表达式的操作进行解析或类型检查。 编译器将有关该操作信息打包在一起,之后这些信息会用于在运行时评估操作。 在此过程中,dynamic 类型的变量会编译为 object 类型的变量。 因此,dynamic 类型只在编译时存在,在运行时则不存在。

  Class

  使用 class 关键字声明类,在 C# 中仅允许单一继承。 也就是说,一个类仅能从一个基类继承实现。 但是,一个类可实现多个接口。类的默认访问标识符是 internal,成员的默认访问标识符是 private

   public class Car : ICar
    {
        public Car()
        {
            Console.WriteLine("对象已经创建");
        }
        public Car(string color)
        {
            Color = color;
        }

        ~Car()
        {
            Console.WriteLine("对象已经消毁");
        }
        public string Color { get; set; }
        public int Speed { get; set; } 

        public void Run()
        {
            Console.WriteLine("Runing");
        }

        public void Stop()
        {
            Console.WriteLine("Stop");
        }
    }

  上述代码中定义了一个Car类,该类有两个构造函数,一个带参数,一个不事参数,默认的构造函数没有任何参数。但是如果你需要一个带有参数的构造函数可以有参数,这种构造函数叫做参数化构造函数。接下来是一个析构函数,类的析构函数是类的一个特殊的成员函数。析构函数的名称是在类的名称前加上一个波浪形(~)作为前缀,它不返回值,也不带任何参数。析构函数用于在结束程序(比如关闭文件、释放内存等)之前释放资源。析构函数不能继承或重载。再下来定义两个属性,然后定义了两个没有返回值方法。在这里我们只简单介绍一下类的声明,关于类的更详细的内容将在后面的章节中介绍。

  Interface

  接口定义协定。 实现该协定的任何 class struct 必须提供接口中定义的成员的实现。 从 C# 8.0 开始,接口可为成员定义默认实现。 它还可以定义 static 成员,以便提供常见功能的单个实现。

接口可以是命名空间或类的成员。 接口声明可以包含以下成员的声明:方法、属性、索引器、事件

上述成员声明通常不包含主体。 从 C# 8.0 开始,接口成员可以声明主体。 这称为默认实现。 具有主体的成员允许接口为不提供重写实现的类和结构提供默认实现。 此外,从 C# 8.0 开始,接口可以包括:常量、运算符、静态构造函数。

嵌套类型、静态字段、方法、属性、索引和事件

使用显式接口实现语法的成员声明,显式访问修饰符。一个接口可从一个或多个基接口继承。 当接口重写基接口中的方法实现时,必须使用显式接口实现语法

    interface ICar
    {
        int Speed { get; set; }
        string Color { get; set; }

        void Run();

        void Stop();
    }

  数组

  数组类型是引用类型,声明数组变量只是为引用数组实例预留空间。 实际的数组实例是在运行时使用 new 运算符动态创建而成。new 运算指定了新数组实例的长度,然后在此实例的生存期内固定使用这个长度。C# 还支持多维数组。

int[] a1 = new int[10];
int[,] a2 = new int[10, 5];
int[,,] a3 = new int[10, 5, 2];

通过 new 运算符,可以使用数组初始值设定项(在分隔符 { } 内编写的表达式列表)指定数组元素的初始值。

int[] a = new int[] {1, 2, 3};

总结

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

欢迎关注我的微信公众号“CSharp开发”

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

“点赞??+赞赏??是最大的鼓励!”

 

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

上一篇:刷题76. Minimum Window Substring


下一篇:基于 WPF 模块化架构下的本地化设计实践