[Day 004 of C#] 复杂的数据类型 —— Sad Beautiful Tragic
Day 004
part 060 - 071.
复杂的数据类型
常量
- 语法
const 变量类型 变量名 = 值;
// 实际上就是声明变量时前面加一个 const 关键字:3
- 常量与变量的区别
- 变量可以被重新赋值,常量不可以。
- 什么时候会用到常量呢?
- 在你要声明某些东西而不想让坏蛋改掉的时候。
枚举
- 语法
public enum 枚举名 { //这里public的中括号在最近framework版本下可以免去
值1,
值2,
值3,
....
值n,
//最后一个值的逗号可有可无
}
// 使用语法
枚举名 变量名 = 枚举名.值x;
- public: 访问修饰符 用于修饰访问权限,而public访问权限是最高的、公开的、哪都可以访问。对于public来说可有可无
- enum: 关键字 声明枚举的关键字
- 枚举名: 要符合Camel的命名规范(Day 002)
- 为什么会有枚举这个东东'w'?
- 假设小冰,[保密],小熊猫在给学校做一个学员管理系统,在系统里面要输入每个学员的信息,可能要输入姓名、性别、年龄、年级等等...
- 而最特殊情况可能就发生在性别身上:
//小冰这样输入性别:
char gender = '女';
//[保密]这样输入性别:
string g3nder = "male";
//小熊猫这样输入性别:
string gend3r = "男人";
- 在上面,分别用了两种类型的变量,但是用了三种类型的值,那么听谁的?
这便用到了枚举,用一个标准的值限定了输入,规范我们的开发.
-
枚举不能在Main函数中去声明
枚举最好声明到命名空间的下,类的上面。=表示这个命名空间下,所有的类都可以使用这个枚举。 -
枚举就是一个变量类型,只是枚举的声明、赋值、使用与众不同,本质上还都是变量,用于存取数据
\[eg.
\]
public enum Gender { //声明了一个枚举,是一个Gender类型的枚举
男,
女,
//最后一个值的逗号可有可无
}
Gender gender = Gender.男; // 存取了一个gender类型的变量,值为'男'。
\[eg.
\]
using System;
namespace Day_004_of_Csharp
{
public enum seasons {
春,
夏,
秋,
冬,
}
public enum state {
单身,
已婚,
热恋,
空虚,
保密,
}
internal class Program
{
public static void Main(string[] args)
{
seasons spring = seasons.春;
state single = state.单身;
}
}
}
枚举 to String / Int...
枚举 to int
using System;
namespace Day_004_of_Csharp {
public enum state {
单身,
已婚,
热恋,
空虚,
保密,
}
internal class Program {
public static void Main(string[] args){
state single = state.单身;
int n =(int)single;
Console.WriteLine(n);
Console.WriteLine((int)state.已婚);
Console.WriteLine((int)state.热恋);
Console.WriteLine((int)state.空虚);
Console.WriteLine((int)state.保密);
Console.ReadKey();
}
}
}
/* 输出:
0
1
2
3
4
*/
由上可知,
- 枚举类型默认可以跟int类型互相转换,即枚举类型与int类型相互兼容 可以直接用 显示类型转换,也就是强制类型转换
using System;
namespace Day_004_of_Csharp {
public enum state {
单身 = 2,
已婚,
热恋,
空虚,
保密,
}
internal class Program {
public static void Main(string[] args){
state single = state.单身;
int n =(int)single;
Console.WriteLine(n);
Console.WriteLine((int)state.已婚);
Console.WriteLine((int)state.热恋);
Console.WriteLine((int)state.空虚);
Console.WriteLine((int)state.保密);
Console.ReadKey();
}
}
}
/* 输出:
2
3
4
5
6
*/
using System;
namespace Day_004_of_Csharp {
public enum state {
单身 = 2,
已婚,
热恋 = 5,
空虚,
保密,
}
internal class Program {
public static void Main(string[] args){
state single = state.单身;
int n =(int)single;
Console.WriteLine(n);
Console.WriteLine((int)state.已婚);
Console.WriteLine((int)state.热恋);
Console.WriteLine((int)state.空虚);
Console.WriteLine((int)state.保密);
Console.ReadKey();
}
}
}
/* 输出:
2
3
5
6
7
*/
由上可知,
- 自己发现:w
同样,我们也可以把int类型强转为枚举类型
using System;
namespace Day_004_of_Csharp {
public enum state {
单身,
已婚,
热恋,
空虚,
保密,
}
internal class Program {
public static void Main(string[] args){
int i = 3;
int j = 8;
QQstate state = (QQstate)i;
QQstate st4te = (QQstate)j;
Console.WriteLine(st3te);
Console.WriteLine(st4te);
Console.ReadKey();
}
}
}
/* 输出:
空虚
8 //遇见转换不了的,不抛异常,直接输出原本的数字。
*/
由上可知:
- 自己发现:w
枚举 to string
将枚举类型转成string类型
- 所有的类型都能够转换成string类型,在这里小冰用Tostring()去转换
using System;
namespace Day_004_of_Csharp {
public enum state {
单身,
已婚,
热恋,
空虚,
保密,
}
internal class Program {
public static void Main(string[] args){
state falling = state.热恋;
string str = falling.ToString();
Console.WriteLine(str);
Console.ReadKey();
}
}
}
// 输出: 热恋
将string类型转成枚举类型
using System;
namespace Day_004_of_Csharp {
public enum state {
单身,
已婚,
热恋,
空虚,
保密,
}
internal class Program {
public static void Main(string[] args){
string str3 = "3";
string str8 = "8";
string strW = "空虚";
//调用Parse函数的目的就是为了让它帮助我们将字符串转换成对应的枚举类型
//如何获得枚举的类型?运用typeof,来拿到枚举的类型。
state iWantYou = (state)Enum.Parse(typeof(state), str3);
state iWantYou2 = (state)Enum.Parse(typeof(state), str8);
state iWantYouW = (state)Enum.Parse(typeof(state), strW);
//调用Parse这个函数,提供两个参数,(获得枚举类型)(转换的值)
//再强调是要转换成state类型的枚举,要与用于接受的枚举变量的类型一致
Console.WriteLine(iWantYou);
Console.WriteLine(iWantYou2);
Console.WriteLine(iWantYouW);
Console.ReadKey();
}
}
}
/*输出:
热恋
8
空虚
//此时要是strW中的值与枚举state里面的值没有一个能够匹配,程序会异常退出。
*/
此时要是strW中的值与枚举state里面的值没有一个能够匹配,程序会异常退出。
- 可以将一个枚举类型的变量跟int类型和string类型互相转换
- 枚举类型默认是跟int类型相互兼容的,所以可以通过强制类型转换(显示类型转换)的语法互相转换
- 当一个int类型的数字转换一个枚举中没有的值的时候,不会抛异常,而是直接将数字显示出来
- 枚举同样也可以跟string类型相互转换,如果将枚举类型转换成string类型,则直接调用ToString()
- 如果将string转换成枚举类型,则需要写这样一行代码
(要转换的枚举类型)Enum.Parse(typeof(要转换的枚举类型),"要转换的string")
- 如果转换的string是数字,就算枚举中没有,也不会抛异常
- 如果转换的string是文本,如果枚举中没有,就会抛出异常。
练习
- 提示用户选择自己的感情状态,并打印到控制台。
using System;
namespace Day_004_of_Csharp {
public enum state {
单身 = 1, //枚举默认从0开始,为了更方便,让枚举从1依次排序
已婚,
热恋,
空虚,
保密,
}
internal class Program {
public static void Main(string[] args){
Console.WriteLine(@"请选择您的感情状态:
1.单身
2.已婚
3.热恋
4.空虚
5.保密");
state iWantYou = (state)Enum.Parse(typeof(state),Console.ReadLine());
//将用户所输入的转换为枚举state的类型,再赋值给所声明的一个枚举state变量iWantYou
Console.WriteLine("您的感情状态是{0}",);
Console.ReadKey();
}
}
}
/* 输出:
请选择您的感情状态:
1.单身
2.已婚
3.热恋
4.空虚
5.保密
(用户输入)3
您的感情状态是热恋
*/
这道题也可以使用switch-case来做,但是我个人感觉超级麻烦..
结构
- 结构可以帮助我们一次性声明多个不同类型的变量
- 语法
//在命名空间的下面
public struct 结构名{//这里public的中括号在最近framework版本下可以免去
//结构名要符合Camel命名规则
成员; //是字段 (字段和变量干的事情都一样的啦)
成员; //是字段
}
//在类的上面
//在Main函数的里面
结构名 变量名;
变量名.成员 = 值;
- 为什么会有结构这个东东'w'?
- 假设小冰,[保密],小熊猫在给学校做一个学员管理系统,在系统里面要输入每个学员的信息,可能要输入姓名、性别、年龄、年级等等...
//小冰这样输入:
string bingName = "小冰";
int bingAge = 14;
char bingGender = '男';
int bingGrade = 8;
- 这样输入,一个学校可能有好多好多人,这样子给变量就要好头疼的说。
- 于是,结构出现了!解决了小冰头疼的问题。
- 结构不能在Main函数中去声明
结构最好声明到命名空间的下,类的上面。=表示这个命名空间下,所有的类都可以使用这个结构。
\[eg.
\]
using System;
namespace Day_004_of_Csharp {
public struct Person { //声明一个结构Person
public string _name;
public int _age;
public char _gender;
//上面这三个变量(字段)的默认访问权限仅限于结构Person的内部,于是乎public
}
internal class Program {
public static void Main(string[] args){
//
Person bingPerson; //声明一个结构Person的变量,用来存小冰的信息
//此时的变量只有bingPerson这一个,类型是结构Person
bingPerson._name = "小冰";
bingPerson._age = "14";
bingPerson._gender = '男';//结构中是char,这里也要单引号
Person redpandaPerson;
//声明一个结构Person的变量,用来存小熊猫的信息
//此时的变量有bingPerson和redpandaPerson,类型是结构Person
redpandaPerson._name = "小熊猫";
redpandaPerson._age = "20";
redpandaPerson._gender = '男';//结构中是char,这里也要单引号
}
}
}
细节
- 字段和变量干的事情都是一样的,都是用来存储数据。
- 字段和变量的区别: 在程序运行的时候,一个变量一个值,一个字段很很多个值
- 在给字段取名字的时候,要区别于变量。规范上必须给每个字段前加上下划线
结构枚举的套娃
using System;
namespace Day_004_of_Csharp {
public struct Person { //声明一个结构Person
public string _name;
public int _age;
public Gender _gender;
//上面这三个变量(字段)的默认访问权限仅限于结构Person的内部,于是乎public
}
public enum Gender{ //声明一个枚举,规范结构中的字段
男,
女,
}
internal class Program {
public static void Main(string[] args){
//
Person bingPerson; //声明一个结构Person的变量,用来存小冰的信息
//此时的变量只有bingPerson这一个,类型是结构Person
bingPerson._name = "小冰";
bingPerson._age = "14";
bingPerson._gender = Gender.男;//结构中是char,这里也要单引号
Person redpandaPerson;
//声明一个结构Person的变量,用来存小熊猫的信息
//此时的变量有bingPerson和redpandaPerson,类型是结构Person
redpandaPerson._name = "小熊猫";
redpandaPerson._age = "20";
redpandaPerson._gender = Gender.男;//结构中是char,这里也要单引号
}
}
}
练习
- 定义一个结构叫MyColor,有三个成员
using System;
namespace Day_004_of_Csharp {
public struct Mycolor { //声明一个结构Person
public int _red; //成员
public int _green; //成员
public int _blue; //成员
//上面这三个字段的默认访问权限仅限于结构Person的内部,于是乎public
}
internal class Program {
public static void Main(string[] args){
MyColor bingColor;
bingColor._red = 255;
bingColor._green = 0;
bingColor._blue = 0;
}
}
}
数组
若要一次性声明多个相同类型的变量,就要用到数组啦!
- 语法
//多种声明数组的语法
数组类型[] 数组名 = new 数组类型[数组长度]; //常用,并没有赋值
数组类型[] 数组名 = { 值1, 值2, 值3, ... 值n-1, 值n }
int[] nums = new int[10]; //常用,并没有赋值
int[] numsTwo = { 1, 2, 3, 4, 5, 6 }; //用大括号,常用,声明长度并赋值
int[] numsThree = new int[3]{ 1, 2, 3 }; //跟上面一样,修改和编写都麻烦
int[] numsFour = new int[]{ 1, 2, 3, 4}; //同上,也麻烦
\[eg.
\]
//要声明10个int类型的变量
int[] nums = new int[10];
//此时声明一个包含10个int类型变量的数组
//这是个变量从0-9进行排序,0-9是他们的序号,被称之为索引(下标)
//这10个变量初值为0
- 当你写了上面这样一行代码之后,就在内存中开辟了连续的10块(取决于数组的长度)空间。
- 我们管每一块空间称之为这个数组的元素
- 数字类型的数组初值是0;string类型的数组初值是null;bool类型的数组初值是False (null与空的区别写在本标题的 细节 里)
|第n块空间||索引(下标)|元素的值|
|:--😐:---😐:---😐
|1|9|0|
|2|8|0|
|3|7|0|
|4|6|0|
|5|5|0|
|6|4|0|
|7|3|0|
|8|2|0|
|9|1|0|
|10|0|0|
- 一个数组的索引(下标)一定从0开始,到 长度 - 1 结束
- 索引(下标) 用于访问数组中的某一块元素。想要去访问数组中的某一块元素,就要通过这个元素所对应的索引(下标)
>>==>
//要声明10个int类型的变量
int[] nums = new int[10];
//此时声明一个包含10个int类型变量的数组
//这是个变量从0-9进行排序,0-9是他们的序号,被称之为索引(下标)
//这10个变量初值为0
num[0] = 1;
num[1] = 2;
num[2] = 3;
num[3] = 4;
num[4] = 5;
num[5] = 6;
nums[6] = 10;
//给下标为6的第7个元素赋值为10
num[7] = 8;
num[8] = 9;
num[9] = 10;
- 像上面这样的编写太麻烦了,要重复地去给数组中的每一个元素赋值,所以我们用for循环(已知循环次数为10次)
//要声明10个int类型的变量
int[] nums = new int[10];
//此时声明一个包含10个int类型变量的数组
//这是个变量从0-9进行排序,0-9是他们的序号,被称之为索引(下标)
//这10个变量初值为0
for (int i = 0; i< nums.Length; i++) { //nums.Length中,Length获取了nums这个数组的长度
nums[i] = i;
}
//我们通过一个循环给数组赋值,同样也可以通过一个循环对数组进行取值
for (int i = 0; i< nums.Length; i++) { //nums.Length中,Length获取了nums这个数组的长度
Console.WriteLine(nums[i]);
}
/* 输出:
0
1
2
3
4
5
6
7
8
9
*/
细节
- 数组也可以重新赋值,取而代旧值
- 通过一个不存在的索引(下标),对一个不存在的元素进行赋值,程序会抛异常,因为超出了索引的界限
- 数组的长度一旦被声明被固定,就无法被改变了
- null和""(空) 的意思是完全不一样的,null表示根本没在内存开辟空间;""(空)意味着在内存中开辟空间了,但存的是一个 空
数组中最的大小总均(:3)
// 先声明一个int类型的数组,并随意赋值
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
int max = nums[0]; //或赋值为int类型的最小值 int.MinValue;
int min = nums[0]; //或赋值为int类型的最大值 int.MaxValue;
// 这样赋值,是为了确保最大值、最小值都是数组中的元素,让if的判断条件必定成立,才能进行比较
// 如果像注释中第二种方法所声明,是为了确保数组中的每一个元素都比假定的最大值大,数组中每一个元素都比假定的最小值小
int sum = 0;
// 思路: 假定一个最大值,跟每一个元素进行比较,只要有一个元素比它大,就把最大值替换成所正比较的元素。 最小值同理
for (int i = 0; i <= nums.Length; i++) {
// 如果数组中当前循环到的元素比我的max还要大,那就把这个元素赋给我的max
if (max < nums[i] ) max = nums[i];
if (min > nums[i] ) min = nums[i];
sum += nums[i];
}
Console.WriteLine("最大值是{0},最小值是{1},总值是{2},均值是{3}", max, min, sum, sum/nums.Length);
Console.ReadKey();
// 输出: 最大值是9,最小值是0,总值是45,平均值是4
- 关于循环中nums[i]的两种理解方式
- 代表数组中当前循环到的元素
- 代表数组中的每个元素
练习
- 一个数组里面都是人名,要分割成: XX|XX|XX|XX|...
string[] names = { "小冰", "保密", "小熊猫", "必应", "草莓" }
string str = null; // null与空不一样,null没有开辟空间
for (int i = 0; i < names.Length-1; i++){
//这里不写names.Length,是因为这样会让最后一个元素后也出现"|",而那是多余的
//所以我们用nums.Length-1, 不遍历最后一个元素,但在最后输出的时候手动加上最后一个元素
str += names[i]+"|"; //遍历每一个元素添加到str中,并在后面加上“|”
}
Console.WriteLine(str+nums[nums.Length-1]);
Console.ReadKey();
- 将字符串数组的元素顺序反转
string[] sentences = { "我", "要", "爱", "死", "%E5%B0%8F%E8%83%BD"};
for (int i = 0; i < sentences.Length/2; i++){
//这里写sentences.Length/2,是因为N个元素再怎么样也只需要声明N/2次(舍掉小数)
// 在交换的过程中,不管这两个要交换的元素下标为几,都可以写成i和Length-1-i表示。
string temp = sentences[i]; //声明一个第三方变量使其等于其中一个元素
sentences[i] = sentences[sentences.Length - 1 -i];
// 使刚刚赋值给temp的那个元素等于另一个元素
sentences[sentences.Length - 1 -i] = temp;
// 使另一个元素等于temp,即原来的那个元素
}
for (int i = 0; i < sentences.Length; i++){
Console.WriteLine(sentences[i])
}
Console.WriteLine();
/* 输出:
%E5%B0%8F%E8%83%BD
要
爱
死
我
*/
冒泡排序
- 就是将一个数组中的元素按照从大到小,或者从小到大的顺序进行排列
- 让第一个元素跟后面的元素依次比较,只要前面的元素大于(或小于)后面的元素,就交换一次
- 继续上述的过程,所以需要用到 for循环的嵌套
// 要使结果是升序排列的(123456789)
int[] nums = { 1, 0, 2, 7, 2, 5, 3, 8, 9, 4};
for (int i = 0; i < nums.Length -1; i++){
for (int j = 0; j < nums.Length - 1 - i; j++) {
//在这里-i之后每次交换的次数都会递减,效率更高
//在实际上,如果不写-i的话,程序就会对比并交换nums.Length -1次
if (nums[j] > nums[j+1]) { //降序排列只需要把这里大于改成小于即刻
int temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
//运用第三方变量来交换数据
}
}
}
for (int i = 0; i < nums.Length; i++) Console.WriteLine(nums[i]);
Console.ReadKey();
- 在上面对数组进行排序时,C#可以这样写,调用sort这个方法
- 但是只能针对数组做一个升序的排列
int[] nums = { 1, 0, 2, 7, 2, 5, 3, 8, 9, 4};
Array.Sort(nums);
- 对数组的排序进行倒序反转:
int[] nums = { 1, 0, 2, 7, 2, 5, 3, 8, 9, 4};
Array.Reverse(nums);
- 所以,如果需要降序排列的话,可以先升序,再reverse
三元表达式
- 语法
表达式1?表达式2:表达式3
- 表达式1一般为一个关系表达式
- 如果表达式1的值为True,则表达式2的值就是整个三元表达式的值
- 如果表达式2的值为False,则表达式3的值就是整个三元表达式的值
表达式2的结果类型必须跟表达式3的结果类型一致,并且也要跟整个三元表达式的结果类型一致
- 基本上与if-else类同,但三元表达式会用就行。
计算两个数字的大小,求出最大的:
Console.WriteLine("请输入第一个数字");
int n1 = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("请输入第一个数字");
int n2 = Convert.ToInt32(Console.ReadLine());
//if-else的做法:
//if (n1 > n2) Console.WriteLine(n1);
//else Console.WriteLine(n2);
//三元表达式的做法:
//int max = n1 > n2 ? n1 : n2;
//Console.WriteLine(max);
产生随机数
- 创建能够产生随机数的对象 (没有对象? new一个)
- 让产生随机数的这个对象调用函数来产生随机数,但也要限定范围
Random r = new Random(); //这行代码就创建了一个对象,叫"r"
//把这条创建对象的语句写在了外面,因为每循环一次就会创建一个对象,空间占用就大了
while (true){
int rNumber = r.Next(1,11); //Next是一个函数,能帮助我们产生随机数。
//这行代码限定了Next产生的随机数在1-11(不含11)中,返回给一个int类型的rNumber
}
这个人上辈子是干嘛的
- 创建能够产生随机数的对象
- 产生随机数,并用switch-case结构进行选择输出。
//生病了,懒!!!