【C#基础】C# 数据类型总结

简介: 编程语言 C# 数据类型的介绍 。
序号 系列文章
0 【C#基础】初识编程语言C#
1 【C#基础】C# 程序通用结构
2 【C#基础】C# 基础语法解析

前言

:smile:大家好,我是writer桑,前面一章已经学习了C#的基础语法,那本章就开始学习C#程序的数据类型,希望看完大家能够有所收获,感谢支持!

数据类型

C# 是一种强类型语言。 每个变量和常量都有一个类型,每个求值的表达式也是如此。每个方法声明都为每个输入参数和返回值指定名称、类型和种类(值、引用或输出)。C# 分别以 值类型引用类型作为类型的两个主要类别。值类型的变量直接包含它们的数据。引用类型的变量存储对数据(称为“对象”)的引用。

一. 值类型(Value types)

值类型的变量包含类型的实例,不同于引用类型的变量包含对类型实例的引用。默认情况下,在分配中,通过将实参传递给方法并返回方法结果来复制变量值。 对于值类型,每个变量都具有其自己的数据副本,对一个变量执行的操作不会影响另一个变量(ref 和 out 参数变量除外)。

代码理解:

using System;

public struct MutablePoint
{
    public int X, Y;

    public MutablePoint(int x, int y) => (X, Y) = (x, y);

    public override string ToString() => $"({X}, {Y})";
}

public class Program
{
    public static void Main(string[] args)
    {
        var p1 = new MutablePoint(1, 2);
        var p2 = p1;
        Console.WriteLine($"{nameof(p1)} after {nameof(p2)} is modified:{p1} ");
        Console.WriteLine($"{nameof(p2)}: {p2}");

        MutateAndDisplay(p2);
        Console.WriteLine($"{nameof(p2)} after passing to a method: {p2}");
    }

    private static void MutateAndDisplay(MutablePoint p)
    {
        p.X = 100;
        Console.WriteLine($"Point mutated in a method: {p}");
    }
}

/*
*   预期的输出: 
*   p1 after p2 is modified:(1, 2)
*   p2: (1, 2)
*   Point mutated in a method: (100, 2)
*   p2 after passing to a method: (1, 2)   
*/ 
  • 从运行结果可知 MutateAndDisplay 方法对传入的参数 p 的修改并不会影响 Main 方法中的 p2 参数。
  • MutateAndDisplay 方法对值类型变量 p 的操作只影响当前代码所处的栈内存的变量,而 Main 方法的 p2 变量和 p 变量又是两个不同的变量。
  • 结论:对值类型变量的操作只影响存储在变量中的值类型实例。

下表列出了 C# 11 中值类型的简单类型:

类型/关键字 描述 范围 默认值 .NET类型
bool 布尔值 True 或 False False System.Boolean
char 16位 Unicode 字符 U+0000 到 U+FFFF '\0' System.Char
float 32 位单精度浮点型 ±1.5 x 10^-45^ 至 ±3.4 x 10^38^ 0.0F System.Single
double 64 位双精度浮点型 ±5.0 × 10 ^-324^ 至±1.7 × 10^308^ 0.0D System.Double
decimal 128 位精确的十进制值,28-29 有效位数 ±1.0 x 10^-28^ 至7.9228 x 10^28 0.0M System.Decimal
sbyte 8 位有符号整数类型 -128 到 127 0 System.SByte
byte 无符号的 8 位整数 0 到 255 0 System.Byte
short 有符号 16 位整数 -32,768 到 32,767 0 System.Int16
ushort 无符号 16 位整数 0 到 65,535 0 System.UInt16
int 带符号的 32 位整数 -2,147,483,648 到 2,147,483,647 0 System.Int32
uint 带符号的 32 位整数 0 到 4,294,967,295 0 System.UInt32
long 64 位带符号整数 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 0L System.Int64
ulong 无符号 64 位整数 0 到 18,446,744,073,709,551,615 0 System.UInt64
nint 带符号的 32 位或 64位整数 取决于(在运行时计算的)平台 0 System.IntPtr
nuint 带符号的 32 位或 64 位整数 取决于(在运行时计算的)平台 0 System.UIntPtr
除了上述值类型的简单类型之外,C# 程序还包括以下用关键字声明的值类型种类:

1,枚举类型(Enumeration)
枚举类型是由基础整型数值类型的一组命名常量定义的值类型。 C# 程序中使用 enum 关键字定义枚举类型并指定枚举成员的名称,示例代码如下:

enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
}

2,结构类型(Structure)
结构类型是是一种可封装数据和相关功能的值类型,C# 程序中使用 struct 关键字定义结构类型,示例代码如下:

public struct MutablePoint
{
    public int X, Y;

    public MutablePoint(int x, int y) => (X, Y) = (x, y);

    public override string ToString() => $"({X}, {Y})";
}

3,元组类型(Tuple)
元组功能提供了简洁的语法来将多个数据元素分组成一个轻型数据结构。 下面的示例演示了如何声明元组变量、对它进行初始化并访问其数据成员:

(double, int) t1 = (4.5, 3);
Console.WriteLine($"Tuple with elements {t1.Item1} and {t1.Item2}.");
// Output:
// Tuple with elements 4.5 and 3.


(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {t2.Count} elements is {t2.Sum}.");
// Output:
// Sum of 3 elements is 4.5. 

4,可为空的值类型(Null)
可为 null 值类型 T?(T 表示泛型)表示其基础值类型T 的所有值及额外的 null 值,示例代码如下:

// 值类型可隐式转换为相应的可为空的值类型 
double? pi = 3.14;
char? letter = 'a';

int m2 = 10;
int? m = m2;

bool? flag = null;

// 可空值类型的数组:
int?[] arr = new int?[10];
一些针对值类型的常用操作:
  • 若要在运行时获取本机大小的整数大小,可以使用 sizeof()。 但是,必须在不安全的上下文中编译代码。示例代码:
Console.WriteLine($"size of nint = {sizeof(nint)}");
Console.WriteLine($"size of nuint = {sizeof(nuint)}");

// 在64位进程中运行时输出
//size of nint = 8
//size of nuint = 8

// 在32位进程中运行时输出
//size of nint = 4
//size of nuint = 4
  • 若要在运行时获取本机大小的整数的最小值和最大值或者取值范围,请将 MinValue 和 MaxValue用作值类型关键字的静态属性,示例代码:
Console.WriteLine($"nint.MinValue = {nint.MinValue}");
Console.WriteLine($"nint.MaxValue = {nint.MaxValue}");

//nint.MinValue = -9223372036854775808
//nint.MaxValue = 9223372036854775807
  • 如果想在运行时实现值类型数据之间的相互转换,请使用隐式转换或者显式转换,示例代码:
// 隐式数据转换举例:int 转换为 long
int a = 11;
long b = a; 

// 显示数据转换:long 转换为 int 
int a1 = 123;
long b1 = a;         
int c = (int)b1;     
  • 如果想在运行时输出值类型的默认值,可以使用 default 运算符生成默认类型值,例如:
Console.WriteLine(default(bool));   // False
  • 如果想在运行时输出值类型数据的 .NET 类型,可以使用 GetType 方法,例如:
bool b = false; 
Console.WriteLine(b.GetType());     // System.Boolean 

二. 引用类型(Reference types)

引用类型的变量存储对其数据(对象)的引用,不同于值类型的变量直接包含其数据。 对于引用类型,多个变量可同时引用同一对象,多个变量之间互相传递的也是对这个对象的引用;因此,对一个变量执行的操作会影响另一个变量所引用的对象(这点区别于值类型)。

代码理解:

using System;

// 类为引用类型 
public class MutablePoint
{
    public int X, Y;

    public MutablePoint(int x, int y) => (X, Y) = (x, y);

    public override string ToString() => $"({X}, {Y})";
}


public class Program
{
    public static void Main(string[] args)
    {
        var p1 = new MutablePoint(1, 2);
        var p2 = p1;
        Console.WriteLine($"{nameof(p1)} after {nameof(p2)} is modified:{p1} ");
        Console.WriteLine($"{nameof(p2)}: {p2}");

        MutateAndDisplay(p2);       // 显示和改变 
        Console.WriteLine($"{nameof(p2)} after passing to a method: {p2}");
    }

    private static void MutateAndDisplay(MutablePoint p)
    {
        p.X = 100;
        Console.WriteLine($"Point mutated in a method: {p}");
    }
}


/*
*   预期的输出: 
*   p1 after p2 is modified:(1, 2)
*   p2: (1, 2)
*   Point mutated in a method: (100, 2)
*   p2 after passing to a method: (100, 2)
*/ 
  • 从输出结果可知 MutateAndDisplay 方法对参数 p 修改的同时也修改了 Main 方法中的 p2 参数。
  • 因为 p2 变量和 p 变量都是引用同一个对象 MutablePoint 类,也就是指向了同一块内存地址,所以 p2 变量对这内存进行修改的同时也修改了 p 变量的内存,两者同时发生变化。
  • 结论:当两个变量引用同一个对象时,一个变量执行的操作会影响另一个变量的运行结果。

以下表格列举出了 C# 内置引用类型:

类型/关键字 描述 .NET 类型
object 对象类型 System.Object
string 字符串类型 System.String
dynamic 动态类型 System.Object
除了上述内置的引用类型之外,C# 程序还包括以下用关键字声明的引用类型:

1,记录类型(Record)
从 C# 9 开始,可以使用 record 关键字定义一个 record 类型,用来提供用于封装数据的内置功能,record 类型用来构建不可变类型和和线程安全对象,简单的示例:

// 声明 record class 引用类型:
public record Person
{
    public string FirstName { get; init; } = default!;
    public string LastName { get; init; } = default!;
};

// 声明 record struct 值类型:
public record struct Point
{
    public double X { get; init; }
    public double Y { get; init;  }
}

2,类类型(Class)
类是将同类对象的共同属性和行为抽象出来形成的一个相对复杂的数据类型,在 C# 程序中使用 class 关键字声明类,示例如下:

class TestClass
{
    //方法,属性,字段,事件,委托
    //和嵌套类到这里。
}

3,接口类型(Interface)
接口定义"协定",继承接口的类或结构体都必须实现接口中所定义的成员,在 C# 编程中使用 interface 关键字定义接口,示例如下:

// 定义接口 ISampleInterface 
interface ISampleInterface
{
    void SampleMethod();
}

// 类 Program 继承接口并实现
public class Program : ISampleInterface
{
    public void SampleMethod()
    {
        throw new NotImplementedException();
    }
}

4,可为空的引用类型(Null)
引用类型 T? 的变量也可以用 null 进行初始化,注意在取消引用之前必须检查变量是否为空, 示例如下:

string notNull = "Hello";
string? nullable = default;
notNull = nullable!;    // 给定为空值 
一些针对引用类型的常用操作:
  • 常见的装拆箱操作,当一个值类型转换为对象类型时,则被称为装箱;相对的,当一个对象类型转换为值类型时,则被称为拆箱,示例如下:
//将整型变量i进行了装箱并分配给对象o。 
int i = 123;
object o = i;

// 将对象o拆箱并分配给对象i; 
i = (int)o;

Console.WriteLine(i);   // 123
  • string 字符串类型常见的相等运算、连接操作、输出指定位置的字符操作,示例如下:
string s1 = "hello, world";
string s2 = "hello, C#";

// 定义相等运算符 == 和 != 比较 string 对象之间是否相等:
Console.WriteLine(s1 == s2);    // False 

// 定义 + 连接运算符连接两个字符串: 
Console.WriteLine(s1 + s2);    // hello, worldhello, C# 

// 定义 [] 运算符可访问字符串指定位置的字符: 
Console.WriteLine(s1[0]);      // h 
  • 当希望变量不在编译期间进行类型检查时, 使用 dynamic 类型的变量表示,dynamic 类型变量在运行期间才进行解析,示例如下:
dynamic d = 20;     // 运行时进行类型检查

三. 指针类型(Pointer types)

C# 程序中在 不安全的上下文中,类型除了是值类型或引用类型外,还可以是 指针类型 , 通过指针类型可以直接操作对象的内存,指针类型声明采用下列形式之一:
//type* identifier;
void* identifier;   // 允许但不建议 

// 又例如:
char* cptr;
int* iptr; 
  • 指针类型不能从对象继承,并且指针类型之间不存在类型转换
  • 指针类型不支持类型装箱和拆箱的操作
  • 指针不能指向引用或包含引用的结构,因为无法对对象引用进行垃圾回收

    • MyType* 类型的指针变量的值为 MyType 类型的变量的地址

(ps:指针类型不在本章进行详细的讨论。)

---

结语

:heavy_check_mark: 以上就是 C# 数据类型的介绍,希望能够对大家有所帮助。望大家多多支持,你们的支持就是笔者创作最大的动力!
目录
相关文章
|
7月前
|
C#
C#学习相关系列之数据类型类的三大特性(二)
C#学习相关系列之数据类型类的三大特性(二)
|
7月前
|
存储 C#
C#数据类型之结构体介绍
C#数据类型之结构体介绍
|
7月前
|
C#
C#数据类型之枚举类型
C#数据类型之枚举类型
178 0
|
7月前
|
存储 编译器 数据处理
C#基础入门之数据类型
C#基础入门之数据类型
|
7月前
|
存储 C# 图形学
【Unity 3D】C#数据类型和变量、命名规范的讲解(附源码)
【Unity 3D】C#数据类型和变量、命名规范的讲解(附源码)
177 1
|
3月前
|
存储 C# 索引
C# 一分钟浅谈:变量与数据类型简介
【9月更文挑战第1天】在 C# 编程中,了解变量与数据类型至关重要。本文详细介绍了 C# 中的值类型(如整数、浮点数、布尔值等)和引用类型(如类、接口、数组、字符串)。通过示例代码展示了变量的声明与使用方法,并针对数据类型转换错误、变量未初始化及数值溢出等常见问题提供了解决方案。正确选择数据类型不仅能提升程序性能,还可避免潜在错误,有助于编写高质量代码。
136 47
|
24天前
|
开发框架 .NET API
以C#一分钟浅谈:GraphQL 数据类型与查询
本文从C#开发者的角度介绍了GraphQL的基本概念、核心组件及其实现方法。GraphQL由Facebook开发,允许客户端精确请求所需数据,提高应用性能。文章详细讲解了如何在C#中使用`GraphQL.NET`库创建Schema、配置ASP.NET Core,并讨论了GraphQL的数据类型及常见问题与解决方案。通过本文,C#开发者可以更好地理解并应用GraphQL,构建高效、灵活的API。
98 64
|
21天前
|
开发框架 .NET 测试技术
C# 一分钟浅谈:GraphQL 数据类型与查询
本文介绍了GraphQL的基本概念、数据类型及查询方法,重点从C#角度探讨了GraphQL的应用。通过Hot Chocolate库的实例,展示了如何在ASP.NET Core中实现GraphQL API,包括安装、定义Schema、配置及运行项目。文中还讨论了常见问题与解决方案,旨在帮助开发者更好地理解和使用GraphQL。
24 2
|
4月前
|
Java C#
C# 和 java 基本数据类型
C# 和 java 基本数据类型
26 0
|
6月前
|
存储 C# 开发者
C# 编程基础:注释、变量、常量、数据类型和自定义类型
C# 编程基础:注释、变量、常量、数据类型和自定义类型