前面学习了C#和.NET渊源、U3D中创建C#脚本,介绍了C#中的变量,下面继续进行C#的学习。
一、运算符
运算符可将简单的操作(加法乘法等)应用于操作数,他们通常返回一个新值,作为分配给变量的操作结果。
大多数运算符是二元的,也就是说,可以同时操作两个操作数,而有些运算符是一元的,只能作用于一个操作数,并且可以在操作数前后应用。除此之外还有三元运算符。
1:一元运算符
有两个常用运算符,分别是++(递增)和- -(递减),他们还有位置的区别
int a=3;
int b=a++;
此时输出结果a为4,b为3
++在变量之后,则运算符在赋值后执行,称为后缀运算符。
int a=3;
int b=++a;
此时输出结果为a=4,b=4
++在变量之前,则运算符在赋值前执行,称为前缀运算符。
2:二元运算符
+,-,*,/运算符
%:取模运算符,B%A后,取得的小数部分,比如A=10,B=20,则B%A=0;
3:赋值运算符
赋值运算符
= 简单赋值运算符,将右边的数赋值给左边
+=,加且赋值运算符,将右边的数加上左边的数,然后赋值给左边
-=,*=,/=,%=,同理如上。
<<=:左移且赋值运算符(乘以2的多少次),>>=:右移且赋值运算符(除以2的多少次方);
4:关系运算符
==:检查两个操作数的值是否相等,如果相等则条件为真。
!=:检查两个操作数的值是否相等,如果不相等则为真。
<、>、<=、>=,同理
5:逻辑运算符
&&:与门,同时满足才为真。
||:或门,有一个真即为真。
!:非门,真→假;加→真。
6:位运算符(在二进制层面对数字进行逻辑运算)
&:与运算符
|:或运算符
^:异或运算符
~:取反运算符
int a=10;
int b=6;
Debug.Log(a&b);
Debug.Log(a|b);
Debug.Log(a^b);
10的二进制数为0001 0010
6的二进制 为0000 0110;
进行&操作时:将对应位置的0和1做&逻辑,即相同为该数,不同为0
对应数:0000 0010 转化为数字2
进行|操作时:将对应位置0和1做|逻辑,即有一个1为1,两个均为0为0
对应数:0001 0110 转化为数字14
进行^操作时:将对应位置0和1做^逻辑,上下两行不一样,返回1,如果相同则返回0.
对应数:0001 0100 转化为数字12
7:三目运算符(条件运算符,它是唯一有3个操作数的运算符,所以有时又称为三元运算符)
如果a>b则返回a,否则返回b
a>b ? a : b
int a,b,c;
a=7;
b=6;
c=(a>b)?a:b;
则返回c=7
二、选择语句
每个应用程序都需要从选项中进行选择,而C#中的两个选择语句是if和switch
1:if语句
if语句通过计算布尔表达式来确定要执行哪个分支,如果为true,则执行If语句块,否则执行else语句块,if语句可以嵌套。
if (expression1)
{}
else if(expression2)
{}
else if(expression3)
{}
else
{}
2:switch语句
switch (value)
case 1:
Debug.Log("1.起床");
break;
case 2:
Debug.Log("2.洗漱");
break;
case 3:
Debug.Log("3.吃饭");
break;
default:
Debug.Log("不知道要做什么");
break;
在switch括号中输入变量,case则可对每个变量的值进行分类讨论。
三、迭代语句
当条件为真时,迭代语句会重复执行语句块,或为每个集合中的每项重复执行语句块。
1:while循环
while循环语句会对布尔表达式求值,并在布尔表达式为true时继续循环。
int a= 0;
while (a<10)
{
Debug.Log(a);
a++;
}
则输出的值为0,1,2,3,4,5,6,7,8,9.
2:do循环语句
do循环语句与while语句类似,只不过布尔表达式是在语句块的底部,而不是顶部进行检查,这意味着语句块总是至少执行一次。
do
{
Write("Enter Your Password");
Passwprd=ReadLine();
}
while("Password != Pa$$word");
WriteLine("Correct!");
此代码下,无论如何,do内代码都会被首先执行一次,再判断while语句的正确性,直到输入正确的password。
3:for循环语句
for循环语句通常与计数器一起使用;
for(int a=1; a<=10; a++)
{
WriteLine(a);
}
for (int i = 0; i <= 100; i++)
if(i==50)
continue;
Debug.Log(i);,//则输出结果中没有50;
Continue 跳过 需求:如果值=50,跳过,但是循环体继续执行
Break 打断 需求:如果值=51,终止循环语句
4:foreach语句(C# 独有的遍历方式)
foreach语句用于对序列(数组或集合)中的每一项执行语句块,序列中的每一项通常是只读的,因此foreach循环也叫做只读循环,如果在循环期间修改序列结构,如添加或删除项,会报错。
foreach(数据类型 变量 in 数组或集合名)
{
循环体
}
string[] names={"a","ab","abc"};
foreach (string name in names)
{
WriteLine("{name} has {name.Length} characters.")
}
四、数据结构
链表和数组作为算法中的两个基本数据结构,在程序设计过程中经常用到。尽管两种结构都可以用来存储一系列的数据,但又各有各的特点。
链表 list 数组
链表的查找效率比较低,只能用first,next,previous等指令查找,不可以直接定位
list和数组是顺序存储,链表的存储是离散的,在删除插入时的效率比较高
1:静态数组:
数组是有序相同的数据类型元素组成的有限集合,内存是线性连续的。
一维数组:当数组中的每个元素都只带一个下标时
二维数组:当数组中每个元素都带两个下标时
....
N维数组:当数组中的每个元素带有N个下标时
①:一维数组
数组必须先定义,后使用。且只能逐个引用数组元素,不能一次引用整个数组。
初始化的几种方式
第①种
int[]array3=new int[4]; //声明为int类型的数组,名称为array3,初始化容量为5
array[0]=10;
array[1]=20;
array[2]=30;
array[3]=40;
第②种
int[]array2=new int[4]{10,20,30,40};
第③种
int[]array1={10,20,30,40};
②:二维数组
初始化的几种方式:
第①种
int[,]array5=new int[2,2];
array5[0,0]=0;
array5[0,1]=1;
array5[1,0]=2;
array5[1,1]=3;
Debug.Log("第一行第二列的元素"+array5[0,1]);
第②种
int[,]array8=new int[2,2];
{
{10,20},
{30,40},
};
第③种:调用指令
Debug.Log("代码的长度(容量)"+array8.Length);
Debug.Log("代码的行数"+array8.Get.Length(0));
Debug.Log("代码的列数"+array8.Get.Length(1));
③:三维数组使用+存储和读取数据;
三维数组可以理解为层+行+列。
int[,,]array10=new int[3,3,3]; //x,y,z
array10[0,0,0]=1;
array10[0,1,0]=2;
array10[0,2,0]=3;
array10[1,0,0]=4;
array10[1,1,0]=5;
array10[1,2,0]=6;
array10[2,0,0]=7;
array10[2,1,0]=8;
array10[2,2,0]=9;
array10[0,0,1]=11;
array10[0,1,1]=21;
array10[0,2,1]=31;
array10[1,0,1]=41;
array10[1,1,1]=51;
array10[1,2,1]=61;
array10[2,0,1]=71;
array10[2,1,1]=81;
array10[2,2,1]=91;
array10[0,0,2]=310;
array10[0,1,2]=320;
array10[0,2,2]=330;
array10[1,0,2]=340;
array10[1,1,2]=350;
array10[1,2,2]=360;
array10[2,0,2]=370;
array10[2,1,2]=380;
array10[2,2,2]=390;
Debug.Log("访问第二层第一行第二列的元素(1,2,1):"+array10[1,2,1]);
Debug.Log("获取array10的数组长度(容量)"+array10.Length);
array10[2,2,1]=910;//修改数据
Debug.Log("访问第二层第二行第二列的元素(2,2,1):"+array10[2,2,1]);
五、动态数组ArrayList和泛型类List
2:动态数组
最初我们使用的都是静态数组,但是,静态数组有着自己难以改变的缺点——数组长度固定。
一般在静态数组定义后,系统就会为其分配对应长度的连续的专有内存空间,可是,我们都知道,
不同的运行样例,所需要的数组长度是不一样的,为了所有样例都可以执行,一般我们会将数组长
度设置为一个很大的值,但是这种方式,满足了一般运行的要求,但是它极大的浪费了内存。
因此我们引入动态数组的概念:
动态数组为对象的有序集合
动态数组会自动调整它的大小
可以使用索引在指定位置添加和移除项目,它也允许在列表中进行动态内存分配。
①第一种构建和打印动态数组的方法
初始化+Add
public class addition : MonoBehaviour
{
ArrayList arrayList1=new ArrayList(); //构建动态数组的第一种方法,对数组进行实例化
void Start()
{
arrayList1.Add(45); //通过add给构建的数组添加元素
arrayList1.Add(25);
arrayList1.Add(12);
Debug.Log(arrayList1[0]); //打印出动态数组的第一,二,三的值
Debug.Log(arrayList1[1]);
Debug.Log(arrayList1[2]);
}
②第二种添加动态数组的方法
AddRange
public class ArrayList1: MonoBehaviour
{
ArrayList arrayList1=new ArrayList(); //构建动态数组的第一种方法,对数组进行实例化
int [] array1 = new int[4] {1,2,3,4};
}
void Start()
{
arrayList1.Add(45); //通过add给构建的数组添加元素
arrayList1.Add(25);
arrayList1.Add(12);
arrayList1.AddRange(array1);
//AddRange:向已有数组的末尾添加数组,它可以让我们要加入的东西一次性加入,而不要每次都加一次
foreach(var v in arrayList1) //此处使用了var,在不确定数据类型的前提下,可以进行使用
{
Debug.Log(v);
}
}
③:动态数组的常用指令
清空指令:
arrayList1.Clear();
包含指令:
if (arrayList1.Contains(12)); //使用contains,返回bool类型,判断是否数组中含有12,
{
Debug.Log("包含元素"+12);
}
判断元素位置指令:
arrayList1.IndexOf(12);
//indexof是用来判断元素在数组中的位置,如上数组,则返回3,如果不包含,则返回为-1;
原始赋值指令:
arrayList1[0]=1; //一号位赋值1
插值指令:
arrayList1.Insert(3,66);
//insert方法是在数组的指定位置之后插入某个元素,比如此时是在第三个位置之后插入66
移除指令:
arrayList1.Remove(12); //对元素12进行删除,如果有多个12,则删除第一个
逆转排序指令:元素倒序排序
arrayList1.Reverse(); //逆转元素,把原数组的顺序从后向前了
大小排序指令:
arrayList1.Sort(); //将数组的元素从小到大进行排列
3:泛型类List
作用与功能近似arraylist
无需装箱拆箱,类型转换
类型安全
自行开发时候使用List更多
public class List : MonoBehaviour
{
List list1=new List(); //List1的声明和初始化
void Start()
{
list1.Add(1);
list1.Add(2);
list1.Add(3);
list1.Count; //此指令来测List的长度
list1.Contains/remove/....ArrayList中的方法List基本都能用;
}
4:链表
数组的优势,在于可以方便的遍历查找需要的数据。在查询数组指定位置(如查询数组中的第4个数据)的操作中,只需要进行1次操作即可。但是,这种时间上的便利性,是因为数组在内存中占用了连续的空间,在进行类似的查找或者遍历时会变得很复杂。
链表的特性使其在某些操作上比数组更加高效。例如当进行插入和删除操作时,链表操作的时间复杂度仅为O(1)。另外,因为链表在内存中不是连续存储的,所以可以充分利用内存中的碎片空间。除此之外,链表还是很多算法的基础,最常见的哈希表就是基于链表来实现的。
链表基础知识总结_zql_3393089的博客-CSDN博客_链表
public class 链表 : MonoBehaviour
{
LinkedList linList=new LinkedList(); //双向链表的定义和实例化
LinkedListNodenode;
void Start()
{
node = linList.AddFirst(1);
linList.AddAfter(node,2); //在node后面添加一个值
linList.AddBefore(node,0); //在node前面添加一个值
Debug.Log(linList.Count); //打印出链表的元素数量
Debug.Log(linList.First.Value); //打印出第一个值(value)
Debug.Log(linList.Last.Value); //打印出最后一个值
if(node.Previous !=null) //node之前的值
{
Debug.Log(node.Previous.Value);
}
if(node.Previous !=null) //node之后的值
{
Debug.Log(node.Next.Value);
}
六、类型转换
我们常常需要在不同类型之间转换变量的值,例如数据通常在控制台以文本形式输出,最初他们是以字符串类型的变量,但是随后需要将他们转化为日期/事件、数字或其他类型的数据。又比如有时需要在整数和浮点数之间进行转换等。
转换也称为强制类型转换,分为隐式和显式两种,隐式是自动进行的,并且安全,显式必须要手动执行,可能会丢失信息,比如精度等。
1:隐式和显式地转换数值
①将int变量隐式转换为double变量是安全的,不会丢失任何信息,但是不能隐式的将double变量转为int变量,这是不安全的。
//隐式将int转为double
int a=10;
double b=a;
//不能隐式将double转为int,此处会丢失精度
double c=9.8;
int d=c;
②使用System.Convert类型进行转换
double a=9.8;
int b=ToInt32(a);
//此处输出的b是10
系统默认的圆整规则是当>=0.5,向上取整;如果<0.5则向下取整,当然也可以使用Round方法进行控制圆整规则。
③:从任何类型转换为字符串
这是最常见的转换,因此所有类型都提供转换为ToString的方法
int a=10;
Debug.Log(a.ToString());
int boolean=true;
Debug.Log(boolean.ToString());
DateTime now=DateTime.Now;
Debug.Log(now.ToString());
object me=new object();
Debug.Log(me.ToString());
④:将字符串转换为数值或日期事件
这是另一种常用的转换,作用与ToString相反,它叫Parse,只有少数类型有Parse方法,它包含所有的数值类型和DateTime。
Int num01 =int.Parse(strNumber);
DateTime birthday =DateTime.Parse(strNumber)
Float num02= float.Parse(strNumber);