文章目录
3. 特殊数据类型
3.1 值类型(Value Types)与引用类型(Reference Types)
C#中数据类型分为值类型和引用类型,在前面所使用到的如int
、float
等均为值类型,而类(class
)、数组(array
)等则属于引用类型。
概述:什么是值类型和引用类型?
值类型:值类型是指我们进行声明时变量标识符指向的是该类型的具体值的类型,他的数据存储于栈区。
值类型包括基本数据类型(如int
、float
等)和结构体(struct
),在赋值和函数传参时将会传入一个自身的新的副本,对参数的操作将不会直接作用于原来的数据。
引用类型:引用类型是我们进行变量声明时标识符指向的是该类型数据所在堆地址的数据类型。
引用类型包括类(class
)、接口(interface
)、字符串(string
)、数组(array
),在赋值和传参时将会传入对物理内存的引用而不是本身的副本,所以对参数的修改会直接影响到原本的数据。
什么是堆栈?
栈是由系统自动分配的一片内存,它具有容量小访问快的特点。
堆是由我们手动申请,它的容量大但是访问速度完全比不上栈内存访问速度,在C/C++语言中,堆内存必须要我们手动进行释放,在C#中由CLR中的垃圾回收机制进行自动释放或手动调用GC(垃圾回收器)进行释放。
值类型和引用类型的区别是什么?
值类型:
- 值类型存储于计算机栈区(少数值太大的情况会存储于堆区),由系统自动分配内存,在出现内存溢出的情况时系统将会舍弃掉溢出部分。
- 值类型变量存储的为具体数据。
- 值类型在传参时会构建一个新的副本,副本的值等于该变量,对参数的改变并不会修改原来变量的值。
引用类型:
- 引用类型存储于计算机堆区,需要我们手动告诉计算机分配内存,如果出现内存溢出将会污染损坏堆区。
- 引用类型变量存储的是该类型实例的引用,而非实例本身。
- 引用类型在传参时会直接传入对实例的引用,对该参数引用进行修改会直接影响到原本的数据。
Console.WriteLine("值类型变量赋值操作");
int a = 3;
int b = a;
b -= 1;
Console.WriteLine($"a:{
a},b:{
b}"); //a:3,b:2
Console.WriteLine("引用类型变量赋值操作");
Person person1 = new("小明");
Person person2 = person1;
person2.name = "小红";
Console.WriteLine($"person1.name:{
person1.name},person2.name:{
person2.name}"); //person1.name:小红,person2.name:小红
参考链接:
值类型 - C# reference | Microsoft Learn
引用类型 - C# reference | Microsoft Learn
c# 值类型和引用类型的区别-CSDN博客
3.2 结构体(struct)
什么是结构体?
结构体是一种特殊的值类型数据类型,它的基本用法与类相近,但是它所占用的内存比类对象要小,所以在专注于数据存储而不是操作的情况下采用结构体将会更加节省内存。
结构体和类的区别?
- struct 是值类型,class 是引用类型
- struct 不能被继承,class 可以被继承
- struct 默认的访问权限是public,而class 默认的访问权限是private.
- struct总是有默认的构造函数,即使是重载默认构造函数仍然会保留。这是因为struct的构造函数是由编译器自动生成的,但是如果重载构造函数,必需对struct中的变量全部初始化。并且struct的用途是那些描述轻量级的对象,例如点、线等,并且效率比较高。class在没有重载构造函数时有默认的无参数构造函数,但是一被重载,默认构造函数将被覆盖。
- struct的new和class的new是不同的。struct的new就是执行一下构造函数创建一个新实例再对所有的字段进行复制。而class则是在堆上分配一块内存然后再执行构造函数,struct的内存并不是在new的时候分配的,而是在定义的时候分配
如何使用结构体?
结构体定义语法如下:
struct Point
{
public int x;
public int y;
public string getPointString() {
return $"({
x},{
y}"; }
}
在使用时,首先通过<struct_type> name = new();
进行声明,通过.
操作符进行内部数据及函数的调用:
Point p = new Point();
p.x = 10;
p.y = 20;
Console.WriteLine(p.getPointString());//(10,20)
参考链接:
结构类型 - C# reference | Microsoft Learn
教程:使用 struct 数据类型和 ref 语言功能减少内存分配。 - C# | Microsoft Learn
【C#】Struct和Class区别 - 知乎 (zhihu.com)
3.3 字符串(string)和StringBuilder
什么是字符串?
- 字符串是一个用于存储多个有序字符(
char
)的只读集合(即在创建后无法进行修改),他的赋值拼接等操作都会新创建一个字符串并将内容写入这个字符串,再更改变量名的指向,并不会修改原来字符串实例的值。 - 是一个类,所以它是一个具有值类型特征的特殊的引用类型。
- 没有最大容量限制,即在创建时可以书写任意长度的字符。与其他语言中的字符串不同,他并不会以
\0
结尾。
如何声明字符串?
字符串声明语法如下:
string str = "Hello World!";
什么是转义字符?
转义字符是将一些编译器规定的功能性符号转换为实际可显示的普通符号或将普通符号转换为具有系统功能的符号的字符。
为什么使用转义字符?
在定义字符串时,对于一些特殊符号(如\
、''
、“”
等),我们无法将其直接填入字符串的值当中,这是需要使用转义字符。
转义字符如何使用?有哪些转义字符?
转义字符的基本使用方法即为在需要填入的符号前加上\
即可,常见的转义字符如下:
转义序列 | 字符名称 | Unicode 编码 |
---|---|---|
\’ | 单引号 | 0x0027 |
\" | 双引号 | 0x0022 |
\\ | 反斜杠 | 0x005C |
\0 | null | 0x0000 |
\b | Backspace | 0x0008 |
\n | 换行 | 0x000A |
\r | 回车 | 0x000D |
\t | 水平制表符 | 0x0009 |
例如我们想要在字符串中书写他对我说:"你这个年龄段怎么睡得着的"
,直接填写进字符串会报错,因为他会将内容中的双引号视为字符串结束的标志:
![image-20240903145055064](https://img-blog.csdnimg.cn/img_convert/39647f2fde6c9d7abb9f24c31bfcfdf5.png)
而如果我们给他加上\
,他则会被视为一个普通字符,不会报错并且能够正常输出:
string str = "他对我说:\"你这个年龄段怎么睡得着的\"";
Console.WriteLine(str);
![image-20240903145248029](https://img-blog.csdnimg.cn/img_convert/f23a1b4a6d6d8503ccac8aa795651966.png)
而如果想要禁用转义字符,只需要在字符串定义时双引号前加上@
即可,例如在上面可以运行的字符串定义前加上@
,将会再次报错:
![image-20240903145455182](https://img-blog.csdnimg.cn/img_convert/397973429d19b79ebe3fb3c469034670.png)
字符串插入及拼接
为什么要进行字符串的插入或拼接?
字符串在声明后不一定一直不变,在实际开发中可能经常要对字符串进行操作,这事就会涉及到字符串的拼接插入等。
有哪些方法进行拼接插入操作?如何进行拼接插入?
**方法一+
拼接:**虽然字符串不是值类型,但他也可以使用+
和+=
操作符,并且它可以拼接非字符类型的内容,具体效果如下:
string str2 = "name:" + "zhangsan" + " age:" + 18; //拼接结果为str2 = "name:zhangsan age:18"
**方法二Format
函数:**String类提供了一个Format
静态函数(public static string Format( string format, Object arg0 )
)让我们可以对字符串进行插入的操作其中format
为被插入字符串,arg0
则为插入的数据列表(可以为任何类型,在插入时都会转换为String再进行插入),需要注意的是,format
字符串中被插入位置必须要用花括号和索引进行占位,且但索引值大于后面列表时程序将会报错,小于列表时未使用的数据将会直接舍弃(索引从前往后不一定必须为从0到n):
//下面的str3拼接后的值为 姓名:张三, 年龄:18, 性别:男
//后面列表中第四个”666“因为并没有被索引应用,所以被直接舍弃
string str3 = string.Format("姓名:{0}, 年龄:{2}, 性别:{1}", "张三", "男", 18, 666);
//直接使用控制台输出该内容时,可以直接舍弃string.Format,输出结果相同
Console.WriteLine("姓名:{0}, 年龄:{2}, 性别:{1}", "张三", "男", 18);
方法三Concat
函数拼接:Concat
函数同样是String类中的一个静态函数,他会将参数中的数据依次进行拼接:
string str4 = string.Concat("姓名:", "张三", " 年龄:", 18); //姓名:张三 年龄:18
**方法四$
字符格式化:**在format
中使用索引进行占位,但并不便于我们直接阅读,如果索引顺序较为混乱,在出现错误是将会难以定位错误位置,而$
拼接将直接使用花括号{}
和前面的变量名进行占位,便于阅读:
string name = "张三";
char sex = '男';
string str5 = $"姓名:{
name},性别:{
sex}"; //姓名:张三,性别:男
**方法五join
拼接:**该函数同样为静态函数,public static string Join( string separator, string[] value )
,其中,separator
为拼接时没个拼接字段之间的分隔符号,value
为需要进行拼接的字符串数组:
int[] num = {
1, 2, 3, 4, 5 };
string str = string.Join(",", num); //1,2,3,4,5
字符串的一些特殊方法
ToUpper():将字符串中所有小写字母转换为大写字母。
ToLower():将字符串中所有大写字母转换为小写字母。
Trim():删除字符串中的前置和后置空格。
LastIndexOf(char/str):返回最后一次出现该字符或字符串的位置索引。
Replace(char/str, char/str):将指定字符或字符串替换为另一字符或字符串。
String.IsNullOrEmpty(str):判断字符串是否为空或null
。
String.IsNullOrWhiteSpace(str):判断字符串是否为null
或空格。
Split(char):以某一字符为标准将字符串进行拆分。
Substring(startIndex[,length]):获取字符串从起始的索引(到目标长度)的子字符串。
参考链接:
C# 字符串(String) | 菜鸟教程 (runoob.com)
字符串 - C# 编程指南 | Microsoft Learn
什么是StringBuilder?
StringBuilder
是.Net
提供的可以在不新建新的字符串而对字符串进行修改的公共类,他位于System.Text
中,本质是一个封装好的动态字符数组。
为什么需要StringBuilder?
C#原本的字符串是一个常量,不能进行修改,再进行对字符串的大量操作时将会产生大量内存垃圾,引起程序卡顿,使用Stringbuilder
可以避免垃圾过多。
StringBuilder如何分配空间/为什么相比于string更节省内存?
StringBuilder
类似于动态字符数组,在最开始就额外分配了多余空间供我们使用,在空间不足时会申请当前空间一倍的新空间并将当前数组搬入新内存。
相比于string
每次修改都会产生内存垃圾,StringBuilder
则只会在容量不足时产生垃圾,频率更低,所以一般情况下更加节省空间。
StringBuilder
有哪些函数,如何使用?
在使用时,首先要引入命名空间System.Text
,并声明StringBuilder
变量,申请空间。
获取容量:通过Capacity
属性获取当前StringBuilder
对象的容量。
获取字符串长度:通过Length
属性直接获取长度。
增加字符:Append(str/char[,repeat])
,将指定字符(串)添加到末尾(并重复)。
var str = new StringBuilder("test");
str.Append(" StringBuilder"); //str变为 "test StringBuilder"
str.Append('*', 6); //str变为 "test StringBuilder******"
插入字符:Insert(index, str)
在指定位置插入字符:
str.Insert(0, "Reihi is "); //str变为 "Reihi is test StringBuilder******"
删除字符:Remove(startIndex, length)
删除指定位置指定长度的字符:
str.Remove(27, 6); //str变为 "Reihi is test StringBuilder"
获取指定索引字符:StringBuilder
不能直接获取某一字符所在位置,只能通过索引器获得指定位置字符:
char c = str[0]; //c 为 `R`
修改字符:可以直接通过索引器修改对应字符的值。
替换字符:使用Replace(str1, str2)
将包含的str1替换为str2:
str.Replace("Reihi", "泠曦"); //str变为 "泠曦 is test StringBuilder"
清除字符串:通过Clear()
清空所有字符:
str.Clear(); //str 被清空,不包含任何内容
3.4 枚举(Enum)
什么是枚举?
- 枚举类型是一组被命名的整型常量的集合,在默认情况下它的大小从0开始并且按照文本顺序依次+1,如果有需要也可以手动显式给它赋值,后续未赋值常量将会依次+1(无情的+1机器)。
- 枚举声明通常在
namespace
语句块或class
语句块中,不可以声明在函数体中。 - 在声明的枚举类型中不可以添加方法。
为什么要使用枚举?
在编码过程中,我们可能需要对一系列的状态、类别等进行判断,如果单纯使用数字并不利于我们理解,而如果单独创建常量并不便于我们在后续需要对某一些类型进行删改时的操作,定义枚举可以使这些类型常量处于一个固定的集合内,并且便于我们查找和删改。
如何声明枚举类型?
声明的基本语法如下:
enum <enum_name>
{
enumeration list
};
例如,我们声明一个类型为Season
的枚举:
enum E_Season
{
Spring, //默认值为0
Summer, //默认值为1
Autumn, //默认值为2
Winter //默认值为3
}
如何创建枚举变量?
声明枚举变量与声明值类型变量一致,语法如下:
<声明的枚举名> <变量名> = <初始值>;
例如,声明一个名称为season
的E_Season
枚举变量,并且打印他直接输出的值和转换为整型后输出的值:
E_Season season = E_Season.Spring;
Console.WriteLine(season); //输出结果为 Spring (默认调用toString()方法)
Console.WriteLine((int)season); //输出结果为 0
枚举相关的数据类型转换?
因为枚举本身为一个整型常量集合,所以它可以直接显示转换为相关的值类型:
float fs = (float)season;
short ss = (short)season;
同时也可以将其转换为字符串,只需要使用toString()
方法:
string str = season.ToString();
额如果有对应的一个字符串,我们也可以将其转化为枚举变量,语法如下:
<enumType> <name> = (<enumType>)Enum.Parse(typeof(<enumType>), <string>);
例如将"Winter"
转换为E_Season
枚举变量:
E_Season season = (E_Season)Enum.Parse(typeof(E_Season), "Winter");
参考链接:
枚举类型 - C# reference | Microsoft Learn
C# 枚举(Enum) | 菜鸟教程 (runoob.com)
用枚举实现摩斯电码转码
namespace MorseCode
{
internal class MorseCode
{
enum E_MorseCode
{
dh = 'A', hddd = 'B', hdhd = 'C', hdd = 'D', d = 'E', ddhd = 'F', hhd = 'G', dddd = 'H', dd = 'I', dhhh = 'J', hdh = 'K', dhdd = 'L', hh = 'M', hd = 'N', hhh = 'O', dhhd = 'P', hhdh = 'Q', dhd = 'R', ddd = 'S', h = 'T', ddh = 'U', dddh = 'V', dhh = 'W', hddh = 'X', hdhh = 'Y', hhdd = 'Z', dhhhh = '1', ddhhh = '2', dddhh = '3', ddddh = '4', ddddd = '5', hdddd = '6', hhddd = '7', hhhdd = '8', hhhhd = '9', hhhhh = '0', ddhhdd = '?', hddhd = '/', hdhhdh = '(', hddddh = '-', dhdhdh = '.',
hdhdh = '@',//开始符
dddddd = '\t',//错误符
dddhd = '\0',//结束符
e = ' ',//空格
}
/// <summary>
/// 将单个摩斯电码转换为对应字符
/// </summary>
/// <param name="code">单个摩斯电码的字符串形式,可以为".-"或"dh"格式</param>
/// <returns>转换后的字符</returns>
public static char GetCharOfMorseCode(string code)
{
return (char)(E_MorseCode)Enum.Parse(
typeof(E_MorseCode),
code.Replace('.', 'd').Replace('-', 'h').Replace('=', 'e'));
}
/// <summary>
/// 将字符转换为摩斯电码
/// </summary>
/// <param name="c">
/// 需要转换的字符其中:<br/>
/// 开始符为 '@'<br/>
/// 错误符为 '\t'<br/>
/// 结束符为 '\n'
/// </param>
/// <returns>对应摩斯电码字符串 (".-"格式)</returns>
public static string GetMorseCodeOfChar(char c)
{
return ((E_MorseCode)((int)c)).ToString()
.Replace('d', '.')
.Replace('h', '-')
.Replace('e', '=');
}
/// <summary>
/// 将指定字符分割的摩斯电码翻译为语句
/// </summary>
/// <param name="code">摩斯电码字符串</param>
/// <param name="split">分隔符(可以有多个分隔符,默认为空格)</param>
/// <returns>翻译后的句子</returns>
public static string GetSentenceOfMorseCode(string code, params char[] split)
{
if (split == null || split.Length == 0) split = [' '];
var codeList = code.Split(split);
var resultList = new List<char>();
bool flag = false;
foreach (string c in codeList)
{
var t = GetCharOfMorseCode(c);
if (t == '(')
{
if (flag)
{
flag = false;
resultList.Add(')');
continue;
}
else
flag = true;
}
resultList.Add(t);
}
return string.Join("", resultList.ToArray()).Replace('e', ' ');
}
/// <summary>
/// 将语句转换为对应摩斯电码
/// </summary>
/// <param name="sentence">需要转换的语句</param>
/// <param name="split">转换为摩斯电码时使用的分隔符(应该避免'.' '-' '='),默认为'|'</param>
/// <returns>对应的摩斯电码</returns>
public static string GetMorseCodeOfSentence(string sentence, char? split)
{
sentence = sentence.Trim().ToUpper();
var resultList = new List<string>();
foreach (char c in sentence)
{
if (c == ')' || c == '(' || c == ')')
resultList.Add(GetMorseCodeOfChar('('));
else if (c == '?')
resultList.Add(GetMorseCodeOfChar('?'));
else
resultList.Add(GetMorseCodeOfChar(c));
}
if (split == null || split == '.' || split == '-' || split == '=') split = '|';
return string.Join((char)split, resultList);
}
}
}
3.5 数组(Arrays)
什么是数组?
- 数组是一个存储一组相同类型数据的集合。
- 数组可以分为一维数组、二维数组、交叉数组(多维数组)。
- 他是一个引用变量。
为什么需要数组?
对于少量数据,我们通过定义少量的变量就可以解决,但是如果对于大量同类型数据我们是无法真的去定义那么多的变量来处理的,例如我们需要统计全国十四亿人的平均年龄,不可能去创建十四亿个整型变量进行运算,直接创建对应长度的数组进行运算会更加方便。
如何使用数组/数组的声明?
基础语法(如果在声明时未进行赋值操作,则仅创建了一个数组名,并未分配实际内存空间):
- 一维数组声明:
<变量类型>[] <数组名> (= {<具体内容>});
- 二维数组声明:
<变量类型>[][] <数组名> (= { {<第一行具体内容>}...{<第n行具体内容>}});
- 交叉数组声明:
<变量类型>[]...[] <数组名> (= {...});
手动申请空间:
- 申请空间但不设置初始值(默认按照对应数据类型初始化初始值):
<变量类型>[] <数组名> = new <变量类型>[申请大小];
- 申请空间并赋值:
<变量类型>[] <数组名> = new <变量类型>[申请大小]{<具体值>};
- 赋值但不明确申请大小(大小将被设置为赋值的数据个数):
<变量类型>[] <数组名> = new <变量类型>[]{<具体值>};
可以对数组进行哪些操作?
数组的使用可以直接通过<数组名>[index]
进行元素访问,其中index
为元素下标(下标从0开始,最大为数组大小size-1),如果下标越界则会引起报错。
**获取数组大小:**当不知道数组具体大小时,我们可以通过array.Length
获取当前操作数组的大小
int[] arr1 = {
1, 2, 3 };
int size = arr1.Length; //size = 3
**获取元素值:**直接通过<数组名>[index]
进行访问,例如在arr1
中,arr1[0]
的值为1,arr1[2]
的值为3.
**获取某一元素下标:**使用Array
类中的IndexOf(Array, Object)
函数,如获取2
的下标:
int index = Array.IndexOf(arr1, 2);//index = 1
遍历数组:(遍历即为获得从某一位置开始一直到某一位置结束的数组中的所有元素值)
-
如果要对整个数组进行遍历,我们可以使用
foreach
语句,例如我们想要遍历arr1
中的所有元素,可以只用如下方法:foreach (int i in arr1) { Console.WriteLine(i); }
-
如果需要指定某一位置开始到某一位置结束时的所有元素,可以使用
for
循环:for (int i = 1; i < arr1.Length; i++) { Console.WriteLine(i); }
-
对于
for/foreach
语句,参考C# for/foreach 循环 | 菜鸟教程 (runoob.com)。
**数组增加/删除元素:**数组初始化后不可以直接进行增加或删除元素的操作,如果需要进行增加元素的操作,只能新建一个比他大小更大的新数组并将原来的值添加到新数组,删除同理(也可以单独设置一个数组大小变量,删除时将未删除元素前移并将代表大小的变量修改为新的值)。
**清除数组元素:**清除数组元素可以使用Clear
函数,他又有两种用法,第一种为清理整个数组元素Clear(array)
,第二种为清理从某一下标开始的所有元素Clear(array, index, length)
,清理结果为将元素置为0
、null
或false
:
int[] arr = {
1, 2, 3, 4, 5 };
Array.Clear(arr, 0, 2);//将从0开始的2个元素置为0,数组变为 { 0, 0, 3, 4, 5 }
Array.Clear(arr);//清除所有元素,数组变为{ 0, 0, 0, 0, 0 }
**复制数组:**使用Copy
函数可以将一个数组的元素复制到另一个数组中,它的语法为Array.Copy(源数组, 复制起始下标, 目标数组, 粘贴起始下标, 复制长度)
:
int[] arr1 = {
1, 2, 3, 4, 5 };
int[] arr2 = new int[9];
Array.Copy(arr1, 3, arr2, 4, 2);//arr2 = { 0, 0, 0, 0, 4, 5, 0, 0, 0 }
Array.Copy(arr1, arr2, arr1.Length);//arr2 = { 1, 2, 3, 4, 5, 0, 0, 0, 0 }
**数组排序Sort
:**使用Sort
函数可以快速对数组排序,默认为递增排序:
int[] arr3 = {
1, 5, 6, 7, 2, 45, 5 };
Array.Sort(arr3);//arr3 = { 1, 2, 5, 5, 6, 7, 45 }
参考链接:
数组 - C# reference | Microsoft Learn
Array 类 (System) | Microsoft Learn
Array.Sort 方法 (System) | Microsoft Learn
C# 数组(Array) | 菜鸟教程 (runoob.com)
3.6 时间结构(DateTime)
什么是时间结构?
时间结构是C#类库提供的用于存储和处理时间数据的结构体,它可以让我们更方便的对时间数据进行处理。
如何使用?
首先要创建对应数据实例,然后再对其进行对应操作。
如何创建时间实例?
直接实例化并赋值:
var dateTime = new DateTime(2024, 9, 4, 15, 45, 0);
Console.WriteLine(dateTime); //2024/9/4 15:45:00
实例化后赋值当前时间:
var dateTime = new DateTime();
dateTime = DateTime.Now;
Console.WriteLine(dateTime); //2024/9/4 15:52:54
若实例化后未进行赋值操作,它的默认值为0001/1/1 0:00:00
。
如何获取不同的时间?
//获取现在的时间
var now = DateTime.Now; //2024/9/4 16:02:45
//获取今天的日期,仅会获取日期,小时及以下单位不会获取
var today = DateTime.Today; //2024/9/4 0:00:00
//获取当前所处小时
var nowHour = now.Hour; //16
//获取当前分钟数
var nowMinute = now.Minute; //2
如何对时间进行修改?
//在当前日期上增加一天
var tomorrow = now.AddDays(1);//2024/9/5 16:02:45
//在当前日期上减少一天
var yesterday = today.AddDays(-1);//2024/9/3 0:00:00
如何按照不同格式输出日期?
Console.WriteLine(now.ToLongDateString()); //2024年9月4日
Console.WriteLine(now.ToShortDateString()); //2024/9/4
Console.WriteLine(now.ToLongTimeString()); //16:13:59
Console.WriteLine(now.ToShortTimeString()); //16:13
Console.WriteLine(now.ToString()); //2024/9/4 16:15:10
//自定义格式输出
Console.WriteLine(now.ToString("yyyy-MM-dd-dddd HH:mm")); //2024-09-04-星期三 16:19
格式化日期字符参考:标准日期和时间格式字符串 - .NET | Microsoft Learn|自定义日期和时间格式字符串 - .NET | Microsoft Learn
参考链接:DateTime 结构 (System) | Microsoft Learn
什么是时间跨度TimeSpan
?
时间跨度是一种特殊时间结构,它用来计算某一个时间点到另一个时间点之间的时间段长度(时间跨度)。
//创建时间跨度并赋值为1:00:00
var timeSpan = new TimeSpan(1, 0, 0); //01:00:00
//以小时为单位创建
timeSpan = TimeSpan.FromHours(2); //02:00:00
//将字符串转化为时间跨度
timeSpan = TimeSpan.Parse("01:20:00");
//获取两个时间点中的跨度
var start = DateTime.Now;
var end = start.AddHours(2).AddMinutes(33);
timeSpan = end - start; //02:33:00
//按照不同格式输出时间跨度
Console.WriteLine(timeSpan); //02:33:00
//获取部分内容
Console.WriteLine(timeSpan.Minutes); //33
//输出总共需要的分钟数
Console.WriteLine(timeSpan.TotalMinutes);//153
//修改时间跨度
timeSpan = timeSpan.Add(TimeSpan.FromMinutes(7));//02:40:00
timeSpan = timeSpan.Subtract(TimeSpan.FromMinutes(40));//02:00:00