目录
C#基础语法专题1
变量、数据类型
简化形式
数组
数组声明
数组初始化几种方式:
①一维数组
②二维数组
③三维数组
小作业:调试函数,更改数组某个下标的值,通过跟踪调试看设置的值有没有生效
运算符
<<n:左移n位,即乘以n的次方
>>n:右移n位,即除以n的次方
C#基础语法专题2
关系逻辑三目运算符
位运算符
枚举
头文件 using System;
String 转换为 Enum 类型
int 转换为 Enum 类型
*用枚举解决switch可读性的问题
switch(mAction)
{
case emAction.Getup:
{
Debug.Log("1.起床");
}
break;
case emAction.Wash:
{
Debug.Log("2.洗漱");
}
break;
case emAction.Eat:
{
Debug.Log("3.吃饭");
}
break;
case emAction.Study:
{
Debug.Log("4.学习");
}
break;
default:
{
Debug.Log("不知道做什么");
}
break;
}
循环
continue :跳过单词循环
break :跳出循环体
C# Class基础(1)
Class构造函数和析构函数
构造函数可以传入参数,析构函数不可以
作用域:
继承
class 子类 : 父类{}
不支持多重继承(不能继承多个父类)
封装
关于如何计算不同图形面积:
①用枚举来做
public enum POLYGON
{
Rectangle,
Triangle,
}
public class Polygon //父类
{
public int Length;
public int Width;
public string color;
public string name;
public float Area;
public POLYGON emPolygon;
public void CalcArea()
{
if(emPolygon == POLYGON.Rectangle)
{
Area = Length * Width;
}
else if(emPolygon == POLYGON.Triangle)
{
Area = Length * Width / 2;
}
}
}
void Start()
{
// 矩形面积 = 长 * 宽
Rectangle rectangle = new Rectangle();
rectangle.emPolygon = POLYGON.Rectangle;
rectangle.Length = 3;
rectangle.Width = 4;
rectangle.name = "矩形";
rectangle.color = "红色";
rectangle.CalcArea();
Debug.Log("矩形面积" + rectangle.Area);
// 三角形面积 = 长 * 宽 / 2
Triangle triangle = new Triangle();
triangle.emPolygon = POLYGON.Triangle;
triangle.Length = 3;
triangle.Width = 4;
triangle.name = "三角形";
triangle.color = "黄色";
triangle.CalcArea();
Debug.Log("三角形面积" + triangle.Area);
}
②用虚函数来做(更改上述代码)
多态
使用 virtual 体现多态
注意:
覆盖和重载的区别
覆盖:发生在继承关系中,通过 virtual 和 override 来实现,函数名和参数都一模一样
重载:发生在任何关系,是通过函数名相同,参数不同实现的(参数顺序、个数都影响)
this和base关键字的区别
即:base访问父类,this访问父类(如果继承了)+当前子类
静态类
静态类一般用于工具类
const:
①const 在声明时就必须赋初值初始化
②因此某种程度上,是不可变值
③在类内声明的 const 常量,外部访问时,必须通过类名
readonly:
①readonly 和 const 不可共存
②只能在声明时或构造函数里初始化,并且不能修改值
③用 readonly 修饰的变量,不会发生 const ③情况(可被实例访问)
变量访问修饰符的控制:get set 以及 value赋值
静态构造函数
①静态构造函数中不允许出现访问修饰符
②如果父类 static 了,那么无论多少实例,父类的构造函数只被系统自动调用一次
静态类
①静态类不允许有实例构造函数,只有一个静态构造函数(并不会被执行)
②静态类不允许被实例化
③静态类里内部成员必须是静态成员/常量
④静态类无法作为分类
抽象类
①不允许实例化
②支持构造函数
③抽象类可继承抽象类
④静态构造函数只执行一次。其他构造函数则根据不同实例分别再次调用
public abstract class MyClass1
{
}
public abstract class MyClass2 : MyClass1
{
public MyClass2() { Debug.Log("MyClass2 默认构造函数"); }
static MyClass2() { Debug.Log("MyClass2 静态构造函数"); }
}
public class MyClass3 : MyClass2
{
public MyClass3() { Debug.Log("MyClass3 默认构造函数"); }
}
/*-------------Abstract类里------------------*/
void Start()
{
MyClass3 myclass3 = new MyClass3();
}
如果实例化了两个对象:
⑤允许 virtual 虚函数
⑥若函数声明为 abstract,则不允许包含函数体,且子类必须覆盖父类的该方法
密封类
C# Class基础(2)
泛型类和泛型方法
public class MyClass6<T>
{
private T[] m_array;
public MyClass6(int size)
{
m_array = new T[size]; // 容量
}
public void Set(int index, T value)
{
m_array[index] = value;
}
public T Get(int index)
{
return m_array[index];
}
}
这个T,它甚至可以是一个类啊!
public class MyClassType
{
public int a;
public MyClassType(int value)
{
this.a = value;
}
}
在Start()函数里的代码:
void Start()
{
MyClass6<int> myclass6 = new MyClass6<int>(5);
myclass6.Set(0, 666);
myclass6.Set(1, 1);
int a = myclass6.Get(0);
int b = myclass6.Get(1);
Debug.LogFormat("第{0}号位,值:{1}", 0, a);
Debug.LogFormat("第{0}号位,值:{1}", 1, b);
MyClass6<string> myclass6_1 = new MyClass6<string>(5);
myclass6_1.Set(0,"你好");
myclass6_1.Set(1, "唐老鸭");
string c = myclass6_1.Get(0);
string d = myclass6_1.Get(1);
Debug.LogFormat("第{0}号位,值:{1}", 0, c);
Debug.LogFormat("第{0}号位,值:{1}", 1, d);
MyClass6<MyClassType> myclass6_2 = new MyClass6<MyClassType>(5);
myclass6_2.Set(0, new MyClassType(1));
myclass6_2.Set(1, new MyClassType(2));
MyClassType e = myclass6_2.Get(0);
MyClassType f = myclass6_2.Get(1);
Debug.LogFormat("第{0}号位,值:{1}", 0, e.a);
Debug.LogFormat("第{0}号位,值:{1}", 1, f.a);
}
得到结果
!!!!改进!!!!
增加上述部分代码后,可以直接引用.a了
此时不用再e.a, f.a,直接引用e, f 即可
注意 where T 中 T 的范围
注意泛型类和泛型方法的运用
接口
不能将函数声明改为私有
不能实例化接口
可以多重继承
类继承接口:需要实现接口功能
如图
抽象类和接口的对比
相同点
不同点
1.变量
接口不允许声明变量,抽象类可以
2.构造函数
接口不允许使用构造函数/静态构造函数,抽象类可以
3.函数实现
接口不允许函数实现,抽象类可以
4.访问修饰符
①接口默认 public 不允许变为private
②抽象类默认 private
函数前若为abstract,那访问修饰符也不能是 private;
非abstract声明函数可以允许private potected
5.多重继承
接口允许多重继承,而抽象类不允许
Struct 结构体
Struct 和 Class 的对比
相同点
1.结构体和类都支持静态构造函数
2.结构体和类都支持自定义函数
不同点
1.结构体不能定义无参构造函数,只允许构造有参构造函数,而类都可以
2.结构体不允许定义析构函数,但是类可以
3.函数修饰符
结构体函数不允许声明为 virtual 虚函数,但类可以
4.结构体类型不允许声明为 abstract,但类可以
5.关于变量
结构体声明的全局普通变量(不带修饰符的),不能在声明里赋值(且必须要赋值),即只能在构造函数里赋值;但是类在哪里都可以
const变量都只能且必须在声明时赋初值(这是个相同点,但是放一起方便总结)
readonly变量
结构体只能在构造函数里赋值,而类都可以
6.关于继承
结构体只能继承自接口,不可以互相继承
但是类与类之间是可以继承的(sealed密封类除外)
7.访问变量
结构体可给变量显示赋值,直接访问成员变量
而类必须实例化对象才可以访问
8. 访问函数
结构体变量和类对象 都必须进行初始化,才可以访问
9.new
结构体的new并不会分配内存,仅仅调用结构体的构造函数初始化而已
类属于引用类型,类的new会在堆上分配内存,也会调用类的构造函数进行初始化
类型转换
引用类型存储在堆上,值类型存储在栈上
is
as
举例:
public class Monster
{
public string name = "骷髅兵";
public int hp = 100;
}
/*---------- HeapStack : MonoBehaviour ---------------*/
void Start()
{
Example1();
}
public void Example1()
{
Monster[] monsters = { new Monster(), new Monster(), new Monster() };
Monster monster1 = monsters[0];
monster1.name = "石头人";
Monster monster2 = monsters[0];
monster2.name = "嗜血蝙蝠";
Debug.Log(monster1.name);
}
原理:通过引用的方式,由地址一层层找到堆里的地址,然后修改堆里的数据,会对其他所有指向该位置的变量产生影响
C# 设计模式专题
委托的定义和使用
和函数相似,只是前面加了delegate,后面不进行函数的实现
含参的使用方式:
返回值的使用方式:
泛型使用方式:
系统内置Action委托
需要使用命名空间 using System;
系统内置Func委托
* Func<T1, T2, T3,...,Tn>,返回值类型将是Tn的类型,非最后一个泛型T的声明,需要与实现函数的参数个数及类型保持一致
同样 需要使用命名空间 using System;
System.Func 可以不带参数,但是必须带一个返回值
/*---------- Start()函数 ----------*/
Func<string, int> func2 = Show2;
/*---------------------------------*/
int Show2(String a)
{
return int.Parse(a);
}
或是
/*-------- Start() ----------*/
Func<int, string> func2 = Show2;
string b = func2(1000);
Debug.Log(b);
/*---------------------------*/
string Show2(int a)
{
return a.ToString();
}
匿名事件、Event事件、多播委托
匿名函数的用法:
多播委托的用法:
void Start()
{
Action action = Show1;
action += Show2;
action();
}
private void Show1()
{
Debug.Log("Show1");
}
private void Show2()
{
Debug.Log("Show2");
}
注意:
则
*此时若再次移除Show1,则会报错,原因如下
因此需要增加一个判断
event事件
不能在函数的内部进行声明
而要在函数外部进行
单例模式
设计模式分类
单例模式定义
——一般应用在管理器中
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MySingleton
{
public MySingleton()
{
Debug.Log("构造函数执行");
}
}
public class Singleton : MonoBehaviour
{
void Start()
{
MySingleton single1 = new MySingleton();
MySingleton single2 = new MySingleton();
MySingleton single3 = new MySingleton();
Debug.Log("single1 = " + single1.GetHashCode() + ",single2 = " + single2.GetHashCode() + ",single3 = " + single3.GetHashCode()); ;
}
}
GetHashCode()得到 不同堆对象引用的地址
下面进行单例设计:
观察者模式
示例程序
/*基类*/
public class Animal
{
protected string Name;
public Animal(string name)
{
this.Name = name;
}
public virtual void Run()
{
}
}
/*猫和老鼠*/
public class Cat : Animal
{
public Action actions; // 发布者
public Cat(string name) : base(name) // 调用基类的构造函数
{
}
public void coming()
{
Debug.Log(Name + "来啦");
if(actions != null)
{
actions(); //通过多播委托,实现一对多的关系
}
this.Run();
}
public override void Run()
{
Debug.Log(Name + "开始追三只老鼠...");
}
}
public class Mouse: Animal
{
public Mouse(string name, Cat cat) : base(name) // 调用基类的构造函数
{
cat.actions += this.Run; // 订阅者
}
public override void Run()
{
Debug.Log(Name + "逃跑");
}
}
public class Visit : MonoBehaviour
{
void Start()
{
Cat cat = new Cat("娘口三三");
//此处 Animal 构成多态
Animal mouseA = new Mouse("老鼠A", cat);
Animal mouseB = new Mouse("老鼠B", cat);
Animal mouseC = new Mouse("老鼠C", cat);
cat.coming();
}
}
若再需增加老鼠,只需增加这一句即可
工厂模式
简单工厂模式
——只处理一个工厂
工厂模式
——可分多个工厂
抽象工厂模式
现在为了避免重名问题,引入命名空间
简单工厂模式
按照这种方式,现在想要增加一个苹果鼠标,就要采用 增加一个AppleMouse类 --> 增加枚举 --> 多态创建appleMouse实例 --> 修改switch 来达成。这违反了开闭原则(不修改原有方法,添加不影响)
因此使用工厂模式(中间件由一变为多)
现在如果还想加入其他类别的产品,比如键盘,为了更加方便管理,使用抽象工厂模式
适配器模式
/*--------------Adaptor------------------*/
//接口用于拓展原始类不足的功能
public interface IAdaptor
{
/// <summary>
/// 统一调用接口,完成不同的行为
/// </summary>
/// <param name="emAdaptorType"></param>
void Charge(AdaptorType emAdaptorType);
}
public class Adaptor : IAdaptor
{
AndroidLine androidLine = new AndroidLine();
IosLine iosLine = new IosLine();
public void Charge(AdaptorType emAdaptorType)
{
if(emAdaptorType == AdaptorType.Android)
{
androidLine.AndroidCharge();
}
else if(emAdaptorType == AdaptorType.IOS)
{
iosLine.IOSCharge();
}
}
}
/*--------------AdaptorMain----------------*/
namespace Adaptor
{
public class AdaptorMain : MonoBehaviour
{
void Start()
{
IAdaptor adaptor = new Adaptor();
adaptor.Charge(AdaptorType.Android);
adaptor.Charge(AdaptorType.IOS);
}
}
}
C# IO操作专题
String常用API
https://www.runoob.com/csharp/csharp-string.html
StringBuilder常用API
https://blog.csdn.net/sibaison/article/details/72356393
String 对象是不可改变的。如果要修改字符串而不创建新的对象,则可以使用 System.Text.StringBuilder 类。
区别:
数据结构
数组
动态数组ArrayList
/*
*一、添加并打印元素
* 方法一:Add()
*/
//定义动态数组
ArrayList arrayList1 = new ArrayList();
arrayList1.Add(45);
arrayList1.Add(25);
arrayList1.Add(12);
foreach(var v in arrayList1)
{
Debug.Log(v);
}
/*
* 方法二:直接添加数组 AddRange()
*/
int[] array1 = new int[4] { 1, 2, 3, 4 };
arrayList1.AddRange(array1);
/*
* 二、清空
*/
arrayList1.Clear();
/*
* 三、判断某元素是否存在
* Contains()
*/
if (arrayList1.Contains(12))
{
Debug.Log("存在元素12");
}
/*
* 四、输出一个元素存在且第一次出现的下标
* IndexOf()
*/
Debug.Log(arrayList1.IndexOf(12));
//若不存在则返回-1
/*
* 五、插入
* Insert()
* 两个参数:插入的下标,插入的元素
*/
arrayList1.Insert(3, 66);
/*
* 六、删除某元素(而不是下标)(只删除第一次出现的)
* Remove()
*/
arrayList1.Remove(12);
/*
* 七、逆转元素————把元素倒序输出
* Reverse()
*/
arrayList1.Reverse();
/*
* 八、排序(从小到大)
* Sort()
*/
arrayList1.Sort();
泛型类集合List<>
List<int> list1 = new List<int>();
用法与ArrayList基本相同,且更优
哈希表
Hashtable ht1 = new Hashtable();
//注意是 Hashtable 而不是 HashTable
具体用法类似ArrayList,例子如下
ht1.Add("1", 100); //Object key, Object Value
ht1.Add(1, 99);
//ht1.Clear();
if(ht1.ContainsKey("1"))
{
Debug.Log("包含Key为" + "1" + "的数据");
}
ht1.Remove("1"); //Key
Debug.Log(ht1["1"]); //取某个键的值的写法为 ht1[key]
Debug.Log(ht1[1]);
ht1[1] = 999; //修改,使用时最好先判断是否Contains再修改
ICollection key = ht1.Keys; //ICollection 接口
foreach(var k in key)
{
Debug.Log(ht1[k]);
}
字典
Dictionary<string, string> dic1 = new Dictionary<string, string>(); //Dictionary<TKey, TValue>
dic1.Add("1", "100");
dic1.Add("2", "200");
dic1.Add("3", "300");
if (dic1.ContainsKey("1"))
{
Debug.Log("键存在");
}
dic1["1"] = "1000"; //修改
ICollection key = dic1.Keys;
foreach(string k in key)
{
Debug.Log(dic1[k]);
}
//另一种遍历写法:
foreach( KeyValuePair<string, string> kvp in dic1)
{
Debug.Log(kvp.Key + " " + kvp.Value);
}
dic1.Remove("2");
dic1.Clear();
*字典和哈希表的区别:
Dictionary在定义时是个泛型,Key和Value都需要指定好类型,则使用时不需要装箱拆箱转换类型的操作,执行效率高;
而Hashtable定义时由于是Object类型,则执行效率肯定会低一些
HashSet
HashSet<int> hs1 = new HashSet<int>();
HashSet<int> hs2 = new HashSet<int>();
hs1.Add(1);
hs1.Add(2);
hs1.Add(2); //由于不能添加重复项,因此不会再添加一个 2
Debug.Log(hs1.Count); //2
hs2.Add(2);
hs2.Add(3);
//hs1.IntersectWith(hs2); //与hs2取交集后的hs1
//hs1.UnionWith(hs2); //取并集
//hs1.ExceptWith(hs2); //取差集
hs1.SymmetricExceptWith(hs2); //取对称差集(去掉共同有的,留下剩下的)
foreach(var v in hs1)
{
Debug.Log(v); //交集后只有元素2
//并集后是 1 2 3
//差集后是只有元素1
//对称差集后是 1 3
}
双向链表
LinkedList<int> linkList = new LinkedList<int>();
LinkedListNode<int> node; //结点
node = linkList.AddFirst(1); //首结点
linkList.AddAfter(node, 2); //在node结点后添加值为2
linkList.AddBefore(node, 0); //首结点变为0
Debug.Log(linkList.Count); //打印链表长度
Debug.Log(linkList.First.Value);
Debug.Log(linkList.Last.Value);
if(node.Previous != null)
{
Debug.Log(node.Previous.Value);
}
if (node.Next != null)
{
Debug.Log(node.Next.Value);
}
链表与List、数组的比较:
List和数组都是顺序存储,存储空间连续;链表的存储空间靠指针连接。因此在执行删除、插入操作时链表效率更高,执行查找操作时链表无法通过索引而只能遍历(O(n)),List和数组都可以直接索引(O(1))
堆栈Stack
先进后出
Stack stk1 = new Stack();
stk1.Push("a");
stk1.Push("b");
stk1.Push("c");
stk1.Push("d");
Debug.Log(stk1.Count);
string v = (string) stk1.Pop(); //Pop出来是Object类型所以需要强制转换
Debug.Log(v);
Debug.Log(stk1.Count);
v = (string) stk1.Peek(); //Peek() 获取栈顶元素但不取出
Debug.Log(v);
Debug.Log(stk1.Count);
Debug.Log("遍历输出:");
foreach(var v2 in stk1)
{
Debug.Log(v2);
}
队列
先进先出
Queue queue = new Queue(); //Object类型
Queue<int> queue2 = new Queue<int>(); //泛型
Debug.Log("Queue2长度:" + queue2.Count);
queue.Enqueue("1"); //入队
queue2.Enqueue(1);
queue2.Enqueue(2);
queue2.Enqueue(3);
int x = queue2.Dequeue(); //队首出队
Debug.Log("出队元素 " + x);
Debug.Log("Queue2长度:" + queue2.Count);
Debug.Log("遍历队列:");
foreach(var v in queue2)
{
Debug.Log(v);
}
实现栈
class MyStack
{
class StackData
{
public StackData nextItem; //下一个结点
public object topData;
public StackData(StackData next, object data)
{
this.nextItem = next;
this.topData = data;
}
StackData top; //栈顶标志
public void Push(object data)
{
top = new StackData(top, data); //更新栈顶
}
public object Pop()
{
object rs1 = top.topData;
top = top.nextItem;
return rs1;
}
}
}
private void Start()
{
MyStack ms = new MyStack();
ms.Push(1);
ms.Push(2);
ms.Push(3);
Debug.Log(ms.Pop());
Debug.Log(ms.Pop());
Debug.Log(ms.Pop());
}
入栈123,出栈321
实现队列
class MyQueue
{
class QueueData
{
public QueueData nextItem;
public object topData;
public QueueData(QueueData last, object data)
{
last.nextItem = this;
this.topData = data;
}
public QueueData(object data)
{
this.topData = data;
}
}
QueueData top;
QueueData lastData;
public void Enqueue(object data)
{
if(top == null)
{
top = new QueueData(data);
lastData = top;
}
else
{
lastData = new QueueData(lastData, data);
}
}
public object Dequeue()
{
object rs1 = top.topData;
top = top.nextItem;
return rs1;
}
}
void Start()
{
MyQueue mq1 = new MyQueue();
mq1.Enqueue(1);
mq1.Enqueue(2);
mq1.Enqueue(3);
Debug.Log(mq1.Dequeue());
Debug.Log(mq1.Dequeue());
Debug.Log(mq1.Dequeue());
}
入队123,出队123
一些VS快捷键
F12:快速访问函数
F9:选中某一行,断点
F11:进入函数体内
F10:单步调试
F5:结束调试
Ctrl + K + C:选择多行注释
Ctrl + K + U:选择多行取消注释
Ctrl + H:替换关键词
C#的部分到此暂告一段落,之后如果有补充知识再更新