目录
1. Directory类的CreateDirectory()方法
简介
*本文档为笔者学习期间产出的笔记存档,内容用于unity学习快速上手C#,笔记根据笔者自身学习情况记录,有很多部分内容不完整,用于方便笔者线上回顾笔记内容,内容包含很多别的博客链接,侵删*
unity官网:Unity - 实时内容开发平台 | 3D、2D、VR & AR可视化
unity开发者社区:首页 - Unity官方开发者社区
unity中文文档:Unity User Manual 2021.3 (LTS) - Unity 手册
Unity使用
创建新项目:Ctrl + N
Tips
Start函数
Unity运行时只执行一次
Updata函数
Unity运行时 会被实时调用
C#
变量
数组
// 数组的申明
int[] array;//没有分配内存
int[] array2 = new int[4];//有内存分配
// 第一种数组初始化
int[] array3 = new int[4];
array3[0] = 10;
array3[1] = 20;
array3[2] = 30;
array3[3] = 40;
// array3[4] = 50; 不可以数组越界
Debug.Log("访问第三个元素(下标为2)代表的数据:" + array3[2]);
array3[2] = 400;
Debug.Log("修正第三个元素(下标为2)代表的数据为400:" + array3[2]);
// 第二种数组初始化
int[] array4 = new int[4] { 100, 200, 300, 400 };//容量显示分配
int[] array5 = new int[] { 100, 200, 300, 400 };//容量隐式分配
//第三种 数组初始化
int[] array6 = { 1000, 2000, 3000, 4000 };
Debug.Log("获取数组的长度 = " + array6.Length);
//-----------------------------------------------
int[,] array7 = new int[2, 2];// 2行2列的数组
array7[0, 0] = 100;
array7[0, 1] = 200;
array7[1, 0] = 300;
array7[1, 1] = 400;
Debug.Log("打印第二行第一列的数据:" + array7[1, 0]);
int[,] array8 = new int[2, 2]
{
{1000, 2000},
{3000, 4000},
};
int[,] array9;
array9 = new int[2, 3]
{
{ 1000, 2000, 9},
{ 3000, 4000, 8},
};
Debug.Log("array9的长度:" + array9.Length);
Debug.Log("array9的行数:" + array9.GetLength(0));//返回指定维度的元素数量
Debug.Log("array9的列数:" + array9.GetLength(1));
调试
F5退出当前断点
F9 添加断点
F10逐行扫描
F11进入函数
折叠区域
#region 被折叠的区域 #endregion
位运算符
枚举
枚举可以提高代码的可读性
与String互相转换
与int互相转换
相对写法1,写法2提高的代码的可读性,知道每一个case的作用是什么,在Unity的面板中可以体现出来
类class
概念
public class MyClass
{
public int a = 10;
public static void Func1()
{
Debug.Log("Func1 Called");
}
}
public class StudybaseClass : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
MyClass myClass = new MyClass();
int b = myClass.a;
Debug.Log(b);
MyClass.Func1();
}
}
在类内部声明的变量或函数,如果想访问,则需要通过类名
static声明的类的变量 称为类的静态变量
static声明的类的函数 称为类的静态函数
构造函数和析构函数
先前已经创建了名叫MyClass的类,如果再想创建一个叫MyClass的类需要创建命名空间
namespace MySpace
{
public class MyClass
{
public int a = 10;
public MyClass()//构造函数 会在类被创建的时候执行
{
Debug.Log("MyCpace 下的 MyClass 构造函数");
}
~MyClass()//析构函数 会在类被删除的时候执行
{
Debug.Log("MyCpace 下的 MyClass 析构函数");
}
public void Show()
{
Debug.Log("MyCpace 下的 Show");
}
}
}
public class StudyBaseClass2 : MonoBehaviour
{
MySpace.MyClass myClass;
void Start()
{
myClass = new MySpace.MyClass();
}
void Update()
{
// Input 是键盘鼠标监听工具类
if (Input.GetMouseButton(0))
{
Destroy(this.gameObject);
}
}
}
访问修饰符
- public 任何公有成员可被外部的类的访问
- private 只有一个类中的函数可以访问它的私有成员
- protected 该类内部和继承类中可以访问
- internal 同一个程序集的对象可以访问
- protected internal 前两个的类型的并集,符合任一规则均可访问
面向对象
继承
将公共的属性或者方法抽离到父类的过程。 这个思维称之为面向对象。
public class Figer//基类/父类
{
public int Length;
public int Width;
public string color;
public string name;
}
//派生类/子类
class Rectangle : Figer//继承自图形类
{
public Rectangle()
{
}
~Rectangle()
{
}
}
class Triangle : Figer
{
public Triangle()
{
}
~Triangle()
{
}
}
封装
把一个或多个项目封闭在一个物理或逻辑的包中
public enum POLYGON
{
Rectangle,
Triangle
}
public class Figer//基类/父类
{
public int Length;
public int Width;
public string color;
public string name;
public float Area;
public POLYGON emPolygon;
public void ShouBaseInfo()
{
Debug.Log("颜色 " + color + " 图形 " + name);
}
public void CalcArea()
{
if (emPolygon == POLYGON.Rectangle)
{
Area = Length * Width;
Debug.Log(Area);
}
else if(emPolygon == POLYGON.Triangle)
{
Area = Length * Width / 2;
Debug.Log(Area);
}
}
public virtual void CalcArea2()//虚函数,子类可以覆盖,若没有覆盖,则调用父类的方法
{
}
}
//派生类/子类
class Rectangle : Figer//继承自图形类
{
//对父类的方法覆盖
public override void CalcArea2()
{
Area = Length * Width;
Debug.Log(Area);
}
public Rectangle()
{
}
~Rectangle()
{
}
}
class Triangle : Figer
{
//对父类的方法覆盖
public override void CalcArea2()
{
Area = Length * Width / 2;
Debug.Log(Area);
}
public Triangle()
{
}
~Triangle()
{
}
}
public class Extern1 : MonoBehaviour
{
Rectangle rect = new Rectangle();
Triangle tri = new Triangle();
// Start is called before the first frame update
void Start()
{
rect.name = "矩形";
rect.color = "黑色";
rect.Width = 3;
rect.Length = 4;
rect.emPolygon = POLYGON.Rectangle;
rect.ShouBaseInfo();
rect.CalcArea();
rect.CalcArea2();
tri.name = "直角三角形";
tri.color = "黄色";
tri.Width = 4;
tri.Length = 4;
tri.emPolygon = POLYGON.Triangle;
tri.ShouBaseInfo();
tri.CalcArea();
tri.CalcArea2();
}
多态
同一个行为具有有不同的表现形式或形态的能力
同一个接口,使用不同的实例而执行不同操作
1.静态多态(编译时)函数和对象的连接机制被称为早期绑定,也被称为静态绑定 C#提供了两种即使来实现 -- 函数重载、运算符重载
在上面封装的例子中函数CalcArea2的实现就是静态多态
2.动态多态(运行时)根据实例对象,执行同一个函数的不同行为
举例:在上面封装的例子中增加下面的函数
public class Figer//基类/父类
{
public virtual void Show()
{
Debug.Log("Polygon Shou");
}
}
class Rectangle : Figer//继承自图形类
{
public override void Show()
{
Debug.Log("Rectangle Shou");
}
}
class Triangle : Figer//继承自图形类
{
public override void Show()
{
Debug.Log("Triangle Shou");
}
}
public class Extern1 : MonoBehaviour
{
Rectangle rect = new Rectangle();
Triangle tri = new Triangle();
// Start is called before the first frame update
void Start()
{
Figer baseParent1 = rect;
Figer baseParent2 = tri;
baseParent1.Show();
baseParent2.Show();
}
}
此时在编译器中按alt点击baseParent1.Show();的Shou()函数,跳转到基类,也就是在编译期无法确认baseParent1.Show()该调用哪个(子类的)方法,运行时才能确定调用哪个方法。
覆盖与重载
覆盖:发生在继承关系中,通过virtual和override实现,函数名以及参数的一模一样的。
重载:发生在任何关系中。只要保证函数名字一样,参数不一样,即可实现重载。
public class Figer//基类/父类
{
public virtual void ShowFunc()
{
Debug.Log("Polygon Shou1");
}
public virtual void ShowFunc(int a, float b)
{
Debug.Log("Polygon Shou2");
}
public virtual void ShowFunc(float a, int b)
{
Debug.Log("Polygon Shou3");
}
}
this和base关键字
this:访问当前类的成员
base:访问基类(父类)的成员
静态类
声明为static。
不能被实例化(使用new关键字创建静态类的实例),不能被继承,只包含静态成员或常量,不能包含实例构造函数,但可以包含静态构造函数,静态构造函数不可被调用。
一般用于工具类
补充
cosnt:
在类内声明的cosnt常量
- 外部访问时,必须通过类名进行访问,但不可修改值
- 只能在声明时初始化
readonly
在类内声明的readonly常量
- readonly和cosnt不能共同修饰一个类型(基本数据类型+自定义数据类型)
- readonly 修饰的类型,可以被类的实例进行访问, 但不能修改值
- readonly 的初始化,只能发生在声明或者构造函数中
public class MyClass3
{
public int mVal;
public const int mVal2 = 100;
public readonly int mVal3 = 10;
public MyClass3()
{
mVal3 = 20;
}
}
public class StaticClass : MonoBehaviour
{
void Start()
{
MyClass3 myClass = new MyClass3 ();
int a = myClass.mVal;
int b = MyClass3.mVal2;
int c = myClass.mVal3;
}
}
变量访问修饰符
可以通过访问修饰符构成的语法块来实现,类似外部只读效果 get set
public class MyClass3
{
//变量访问修饰符
public int mVal4 { get; private set; }
private int m_Val5;
public int mVal5
{
get
{
return m_Val5;
}
set
{
m_Val5 = value;
}
}
public MyClass3()
{
}
}
public class StaticClass : MonoBehaviour
{
void Start()
{
int d = myClass.mVal4;
myClass.mVal5 = 1000;
}
}
静态构造函数
- 静态构造函数不需要添加访问修饰符
- 静态构造函数不论多少实例都只调用一次,而且只被系统自动调用一次
public class MyClass4
{
public MyClass4()
{
Debug.Log("父类构造函数执行");
}
}
public class MyClass3 : MyClass4
{
public MyClass3()
{
Debug.Log("子类构造函数执行");
}
}
public class StaticClass : MonoBehaviour
{
void Start()
{
MyClass3 myclass1 = new MyClass3();
MyClass3 myclass2 = new MyClass3();
}
}
修改MyClass4的构造函数为静态构造函数
public class MyClass4 { static MyClass4() { Debug.Log("父类构造函数执行"); } }
静态类
- 静态类不允许有实例构造函数,只允许存在一个静态构造函数,(暂记:静态类的静态构造函数,测试发现不会执行)
- 静态类不允许被实列化
- 静态类内部成员必须是静态成员或者常量(const)
- 静态类无法作为基类被派生
public class MyClass4
{
static MyClass4()
{
Debug.Log("父类构造函数执行");
}
}
抽象类
声明abstract
- 不能被实例化,意味着被new关键字创建实列
- 可只提供部分函数实现,也可仅声明抽象函数
- 支持构造函数 允许 virtual 虚函数
- 抽象类中的抽象方法函数没有函数体(实现方法),必须在子类中覆盖此方法
密封类
声明sealed
特点:
- 不能被继承,但可以继承别的类或者接口
- 密封类不能声明为抽象类
- 密封类内的成员函数,不能声明成 sealed
一般用于 防止重写某些类或者接口影响功能的稳定
泛型类
在类名后增加吧一个类变成泛型类,泛型T1, T2可被where关键字限定类型。
在创建实例化时必须指定T类型,如果子类也是泛型的,那么继承的时候可以不指定具体类型
用于处理一组功能相同,仅类型不同的任务时
public class MyClass15<T>
{
private T[] m_array;
public MyClass15(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];
}
}
public class TClass : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
MyClass15<int> myClass = new MyClass15<int>(5);
myClass.Set(0, 1);
myClass.Set(1, 2);
int a = myClass.Get(0);
int b = myClass.Get(1);
Debug.Log(a + " " + b);
MyClass15<string> myClass2 = new MyClass15<string>(10);
myClass2.Set(0, "hello");
myClass2.Set(1, "world");
string a2 = myClass2.Get(0);
string b2 = myClass2.Get(1);
Debug.Log(a2 + " " + b2);
}
}
用where关键字限定T的类型
.NET含有以下五种泛型的约束
where T:class T必须是一个类
where T:struct T必须是一个结构体
where T:new() T必须要有一个无参数的构造函数
where T:NameOfBaseClass T必须继承名为NameOfBaseClass的类
where T:NameOfInterface T必须实现名为NameOfInterface的接口
public class MyClassType
{
public int a;
public MyClassType(int val)
{
this.a = val;
}
}
public class MyClass15<T> where T : MyClassType
{
private T[] m_array;
public MyClass15(int size)
{
m_array = new T[size];
}
public void Set(int index, T value)
{
m_array[index] = value;
}
public int Get(int index)
{
return m_array[index].a;
}
}
public class TClass : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
MyClass15<MyClassType> myClass = new MyClass15<MyClassType>(5);
myClass.Set(0, new MyClassType(1));
myClass.Set(1, new MyClassType(2));
int a = myClass.Get(0);
int b = myClass.Get(1);
Debug.Log(a + " " + b);
}
}
接口
interface + name 这将变成一个接口
interface BaseInterface1
{
void ShowWindow();//默认就是public且只能是public
void HideWindow();
}
interface BaseInterface2
{
void PlaySond();
void CloseSound();
}
interface MyInterface : BaseInterface1, BaseInterface2//接口允许单一继承,也可以多重继承
{
}
public class MyClass16 : BaseInterface1, BaseInterface2
{//类也也可以继承多个接口
public void CloseSound()
{
throw new System.NotImplementedException();
}
public void HideWindow()
{
throw new System.NotImplementedException();
}
public void PlaySond()
{
throw new System.NotImplementedException();
}
public void ShowWindow()
{
throw new System.NotImplementedException();
}
//void BaseInterface1.HideWindow()
//{
// throw new System.NotImplementedException();
//}
//void BaseInterface1.ShowWindow()
//{
// throw new System.NotImplementedException();
//}
}
接口与抽象类的区别
相同点:
- 都可以被继承
- 都不能被实例化
- 都可以包含方法声明
- 派生类必须实现未实现的方法
不同点:
- 抽象类可以定义字段、属性、方法实现;接口只能方法声明,不能包含字段
- 抽象类是一个不完整的类,需要进一步细化;而接口是一个行为规范
- 抽象类只能被单一继承;接口可被多重继承
- 抽象类更多的是定义一系列紧密相关的类间;而接口大多数是关系疏松但都实现某一功能的类中
结构体struct
strict+name{……}
结构体的特点:
- 结构体可带有方法、字段、索引、属性、运算符方法和事件
- 结构体可定义构造函数,但不能定义析构函数。不能定义无参构造函数。无参构造函数是自动定义的,且不能被改变
- 与类不同,结构不能继承其他的结构或类,但是可实现一个或多个接口
- 结构不能作为其他结构或类的基础结构
- 结构成员不能指定为abstract、virtual 或 protected
- 结构体不用通过new来实例化
与class对比
- 不允许定义无参构造函数(可以有参)
- 不能定义析构函数
- 结构体含函不允许声明 virtual 虚函数
- 结构体的类型不能声明为抽象的 abstract
- 变量
- 普通变量:结构体声明的全局普通变量(没有修饰符),不能在声明里赋值,但是类哪里都可以
- const:结构体和类对于const修饰的变量没有区别
- readonly:结构体声明的全局readonly变量,只能在构造函数里面赋值,class都可以
- 结构体之间不能互相继承
- 使用上
- 访问变量 class必须要实列化对象才可以访问
- 访问函数 结构体变量和类对象 必须进行初始化才能访问
- new
- 结构体属于值类型。 结构体的new并不会在堆上分配内存,仅调用结构体的构造函数初始化
- 类属于引用类型,类的new 会在堆上分配内存,而且也会调用类的构造函数进行初始化
类型转换
知识补充
引用类型: string int[](数组) class interface
值类型: byte (u)short (u)int (u)long enum struct
is:检查对象类型的兼容性,并返回结果true(false)
void Start()
{
string a = "abc";
int b = 123;
bool c = a is string;
if (c)
{
Debug.Log("是string类型");//是string类型
}
}
as:检查对象类型的兼容性,并返回转换结果,如果不兼容则返回nuIl
void Start()
{
string a = "abc";
string c = a as string;
Debug.Log(c);//abc
}
强制(显式)类型转换 形式通过(Type)a来表示,一般用于高精度数据类型转换为低精度数据类型。
隐式(自动)类型转换 发生在 低精度数据类型自动转换高精度数据类型。
装箱和拆箱
装箱:值类型转换成引用类型
拆箱:引用类型转换成值类型
int a = 20;
object b = (object)a;//装箱 发生GC(内存分配)
object d = a;
int c = (int)b;//拆箱
堆和栈
深入理解C#中的堆(Heap)与栈(Stack),一次性全都掌握!
深入理解C#中的堆(Heap)与栈(Stack),一次性全都掌握!_c# 堆栈-CSDN博客
设计模式
委托
如果要把方法作为函数来进行传递,就需要委托。简单来说,委托是一个类型,这个类型可以赋值一个方法的引用。c#的委托通关dalegate关键字来声明。
声明委托:
dalegate void MyDelegate1(int x)
delegate void MyDelegate2(T x)
使用委托:
MyDelegate1 mydelegate = new MyDelegate(func)
MyDelegate1 mydelegate = func
public class BaseDelegate : MonoBehaviour
{
public delegate void ShowDelegate();
public delegate void ShowDelegate2(int a, int b);
public delegate int ShowDelegate3();
public delegate void ShowDelegate4<T>(T a);
void Start()
{
ShowDelegate show1 = Show1;
show1();
//show1.Invoke();
ShowDelegate2 show2 = Show2;
show2(1, 2);
ShowDelegate3 show3 = Show3;
int a = show3();
Debug.Log(a);
ShowDelegate4<string> show4 = Show4;
show4("Hello");
}
private void Show1()
{
Debug.Log("Show1");
}
private void Show2(int a, int b)
{
Debug.Log(a + b);
}
private int Show3()
{
return 1000;
}
void Show4(string a)
{
Debug.Log("Show4-> a:" + a);
}
}
Action委托
Action是.NET Framework内置的泛型委托,可以使用Action委托以参数形式传递方法,而不用显示声明自定义的委托。封装的方法必须与此委托定义的方法签名相对应。也就是说,封装的方法必须具有一个通过值传递给它的参数,并且不能有返回值。 还有一种是非泛型委托Action。
注意点
Action委托至少0个参数,至多16个参数,无返回值。
Action 表示无参,无返回值的委托。
Action表示有传入参数int,string无返回值的委托。
Action表示有传入参数int,string,boo1无返回值的委托。
Action表示有传入4个int型参数,无返回值的委托。
void Start()
{
Action action1 = Show1;
action1();
Action<int, int> action2 = Show2;
action2(2, 3);
}
void Show1()
{
Debug.Log("Show1");
}
void Show2(int a, int b)
{
Debug.Log(a + b);
}
Func委托
Func是.NET Framework内置的带有返回类型的泛型委托。
注意点:
Funo至少0个钟入参数,至多16个输公参数,根据返回值泛型返回。必须有返回值,不可void.
Func
Func表示传入参数为T1.T2..T3(泛型),返回值为int类型的委托。
void Start()
{
Func<string> func1 = Show1;
string a = func1();
Debug.Log(a);
Func<int, string> func2 = Show2;
string b = func2(1000);
Debug.Log(b);
}
string Show1()
{
return "Show1";
}
string Show2(int a)
{
return a.ToString();
}
int Show3(string a)
{
return int.Parse(a);
}
匿名方法
没有名字的方法称之为匿名方法
Action action = delegate()
{
Debug.Log("匿名函数被执行");
}
action();
Event事件
Event事件本身就是一种委托,只是该委托只能作为类的成员,且不可在类外进行调用
只允许作为类的成员变量,且仅在类的内部使用,外部不得直接调用。
当作为A类的成员,event事件在外部赋值时,只能通过 += 的方法
event Action action;
多播委托
在C#语言中,多播委托是指在一个委托中注册多个方法,在注册方法时可以在委托中使用加号运算符或者减号运算符来实现添加或者撤销方法
Action action = Show1();
action += Show2();
action();
# 这样就会同时执行Show1和Show2函数的内容
action -= Show2();
action();
# 这时只执行Show1
单例模式
一个类只能有一个实例,类只能在内部实列一次,外部不能对此实列化。全局唯一
public class MySingleton
{
private static MySingleton instance;
public static MySingleton Instance
{
get
{
if (instance == null)
instance = new MySingleton();
return instance;
}
}
public MySingleton()
{
Debug.Log("构造函数执行");
}
public void Show()
{
Debug.Log("Show");
}
}
public class Singleton : MonoBehaviour
{
void Start()
{
MySingleton single1 = MySingleton.Instance;
single1.Show();
MySingleton single2 = MySingleton.Instance;
single2.Show();
MySingleton single3 = MySingleton.Instance;
single3.Show();
Debug.Log(single1.GetHashCode());
Debug.Log(single2.GetHashCode());
Debug.Log(single3.GetHashCode());
}
}
观察者模式(发布/订阅模式)
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。此种模式通常被用来实现拿件处理系统。
发布者+订阅者=观察者模式
public class Animal
{
public 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 Conming(/*Animal mousea, Animal mouseb, Animal mousec*/)
{
Debug.Log(name + "来了");
//mousea.Run();
//mouseb.Run();
//mousec.Run();
this.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 mouseA = new Mouse("A", cat);
Animal mouseB = new Mouse("B", cat);
Animal mouseC = new Mouse("C", cat);
cat.Conming(/*mouseA, mouseB, mouseC*/);
}
}
工厂模式
C#设计模式(1)——简单工厂模式 - Frank_520 - 博客园
简单工厂
属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。
特点
只生产一种品牌(类型)的产品,在工厂中动态创建。
工厂模式
避免简单工厂模式中,新增产品品牌(类型)时,直接修改工厂。为了解决这个问题出现了工厂模式。
特点
只生产一种品牌(类型)的产品,在具体的子类工厂中创建。
抽象工厂模式
为了解决系列产品的问题,就有了抽象工厂模式。
适配模式
C#设计模式(7)——适配器模式 - Frank_520 - 博客园
使用相同的接口,完成不同的操作
IO操作
String常用API
string str = "xxx"
str.Length
在当前的 String 对象中获取字符数。
1 | public static int Compare( string strA, string strB ) return : 0 两个字符串相等,非0 两个字符串不相等 比较两个指定的 string 对象,并返回一个表示它们在排列顺序中相对位置的整数。该方法区分大小写。 |
2 | public static int Compare( string strA, string strB, bool ignoreCase ) 比较两个指定的 string 对象,并返回一个表示它们在排列顺序中相对位置的整数。但是,如果布尔参数为真时,该方法不区分大小写。 |
3 | public bool Equals( string value ) 判断当前的 string 对象是否与指定的 string 对象具有相同的值。 |
4 | public static string Concat( string str0, string str1 ) 连接两个 string 对象。 public static string Concat( string str0, string str1, string str2 ) 连接三个 string 对象。 |
5 | public static string Format( string format, Object arg0 ) 把指定字符串中一个或多个格式项替换为指定对象的字符串表示形式。 |
6 | public bool Contains( string value ) 返回一个表示指定 string 对象是否出现在字符串中的值。(不能是字符char) |
7 | public int IndexOf( char value ) 返回 指定 Unicode 字符 在当前字符串中第一次出现的索引,索引从 0 开始。 public int IndexOf( string value ) 返回指定 字符串 在该实例中第一次出现的索引,索引从 0 开始。 |
8 | public int LastIndexOf( char value ) 返回指定 Unicode 字符在当前 string 对象中最后一次出现的索引位置,索引从 0 开始。 public int LastIndexOf( string value ) 返回指定字符串在当前 string 对象中最后一次出现的索引位置,索引从 0 开始。 |
9 | public bool StartsWith( string value ) 判断字符串实例的开头是否匹配指定的字符串。 |
10 | public bool EndsWith( string value ) 判断 string 对象的结尾是否匹配指定的字符串。 |
11 | public string Insert( int startIndex, string value ) 返回一个新的字符串,其中,指定的字符串被插入在当前 string 对象的指定索引位置。 |
12 | public static bool IsNullOrEmpty( string value ) 指示指定的字符串是否为 null 或者是否为一个空的字符串。 |
13 | public string Remove( int startIndex, int count ) 从当前字符串的指定位置开始移除指定数量的字符,并返回字符串。 |
14 | public string Replace( char oldChar, char newChar ) 把当前 string 对象中,所有指定的 Unicode 字符替换为另一个指定的 Unicode 字符,并返回新的字符串。 |
15 | public string[] Split( params char[ ] separator ) 返回一个字符串数组,包含当前的 string 对象中的子字符串,子字符串是使用指定的 Unicode 字符数组中的元素进行分隔的。 |
16 | public string ToLower() 把字符串转换为小写并返回。 |
17 | public string ToUpper() 把字符串转换为大写并返回。 |
18 | public string Trim() 移除当前 String 对象中的所有前导空白字符和后置空白字符。 |
StringBuilder常用API
String 对象是不可改变的。每次使用 System.String 类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。在需要对字符串执行重复修改的情况下,与创建新的 String 对象相关的系统开销可能会非常昂贵。如果要修改字符串而不创建新的对象,则可以使用 System.Text.StringBuilder 类。例如,当在一个循环中将许多字符串连接在一起时,使用 StringBuilder 类可以提升性能。
设置容量和长度
StringBuilder MyStringBuilder = new StringBuilder("Hello World!", 25);
另外,可以使用读/写 Capacity 属性来设置对象的最大长度。以下代码示例使用 Capacity 属性来定义对象的最大长度。
MyStringBuilder.Capacity = 25;
当达到容量时,将自动分配新的空间且容量翻倍。
常用API
Append 方法可用来将文本或对象的字符串表示形式添加到由当前 StringBuilder 对象表示的字符串的结尾处。
StringBuilder MyStringBuilder = new StringBuilder("Hello World!");
MyStringBuilder.Append(" What a beautiful day.");
Console.WriteLine(MyStringBuilder);
此示例将 Hello World! What a beautiful day. 显示到控制台。
AppendFormat 方法将文本添加到 StringBuilder 的结尾处,而且实现了 IFormattable 接口,因此可接受格式化部分中描述的标准格式字符串。
StringBuilder MyStringBuilder = new StringBuilder("Hello World!");
MyStringBuilder.Append(" What a beautiful day.");
Console.WriteLine(MyStringBuilder);
此示例将 Hello World! What a beautiful day. 显示到控制台。
Insert 方法将字符串或对象添加到当前 StringBuilder 中的指定位置。
StringBuilder MyStringBuilder = new StringBuilder("Hello World!");
MyStringBuilder.Insert(6,"Beautiful ");
Console.WriteLine(MyStringBuilder);
此示例将 Hello Beautiful World! 显示到控制台。
可以使用 Remove 方法从当前 StringBuilder 中移除指定数量的字符,移除过程从指定的从零开始的索引处开始。
StringBuilder MyStringBuilder = new StringBuilder("Hello World!");
MyStringBuilder.Remove(5,7);
Console.WriteLine(MyStringBuilder);
此示例将 Hello 显示到控制台。
使用 Replace 方法,可以用另一个指定的字符来替换 StringBuilder 对象内的字符。
StringBuilder MyStringBuilder = new StringBuilder("Hello World!");
MyStringBuilder.Replace('!', '?');
Console.WriteLine(MyStringBuilder);
此示例将 Hello World? 显示到控制台
String和StringBuilder的区别
string本身是不可改变的,它只能赋值一次,每一次内容发生改变,都会生成一个新的对象,然后原有的对象引用新的对象,而每一次生成新对象都会对系统性能产生影响,这会降低.NET编译器的工作效率。
而StringBuilder类则不同,每次操作都是对自身对象进行操作,而不是生成新的对象,其所占空间会随着内容的增加而扩充,这样,在做大量的修改操作时,不会因生成大量匿名对象而影响系统性能。
FileStream文件读写
创建 FileStream 类对象的语法格式如下所示:
FileStream = new FileStream(, , , );
*参数说明见链接
public class FileStreamTest : MonoBehaviour
{
void Start()
{
CreatFile();
ReadFile();
}
void CreatFile()
{
string path = @"F:\Unity\Projects\24.6.16 text1\Assets\Resources\student.txt";
FileStream fileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
string msg = "1710026";
// 字符串转换为字节数组
byte[] bytes = Encoding.UTF8.GetBytes(msg);
//向文件中写入字节数组
fileStream.Write(bytes, 0, bytes.Length);
//刷新缓冲区
fileStream.Flush();
fileStream.Close();
}
void ReadFile()
{
string path = @"F:\Unity\Projects\24.6.16 text1\Assets\Resources\student.txt";
if (File.Exists(path))//判断文件是否存在
{
FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
byte[] bytes = new byte[fileStream.Length];
fileStream.Read(bytes, 0, bytes.Length);
//将得到的字节型数组重写编码为字符型数组
string s = Encoding.UTF8.GetString(bytes);
Debug.Log("学生学号为:" + s);
fileStream.Close();
}
}
}
StringReader/Writer文件读写
public class FileStreamTest : MonoBehaviour
{
void Start()
{
CreatFile();
ReadFile();
}
void CreatFile()
{
string path = @"F:\Unity\Projects\24.6.16 text1\Assets\Resources\student2.txt";
StreamWriter streamWriter = new StreamWriter(path);
streamWriter.WriteLine("123");
streamWriter.WriteLine("你好");
//刷新缓冲区
streamWriter.Flush();
streamWriter.Close();
}
void ReadFile()
{
string path = @"F:\Unity\Projects\24.6.16 text1\Assets\Resources\student2.txt";
if (File.Exists(path))//判断文件是否存在
{
StreamReader streamReader = new StreamReader(path);
while (streamReader.Peek() != -1)
{
string str = streamReader.ReadLine();
Debug.Log(str);
}
streamReader.Close();
}
}
}
Directory/DirectoryInfo操作文件夹
一、判断文件夹是否存在
1.Directory类的Exists()方法
Exists()方法用于确定给定路径是否引用磁盘上的现有目录,语法如下。
public static bool Exists (string path)
☑ path:要测试的路径。
☑ 返回值:如果path引用现有目录,则为true;否则为false。允许path参数指定相对或绝对路径信息。
相对路径信息被解释为相对于当前的工作目录。
//使用Directory类的Exists()方法判断C盘根目录下是否存在Test文件夹。
Directory.Exists("C:\\Test ");
2. DirectoryInfo类的Exists属性
获取指示目录是否存在的值,语法如下。
public override bool Exists{get;}
属性值:如果目录存在,则为true;否则为false。
//调用DirectoryInfo类中的Exists属性判断C盘根目录下是否存在Test文件夹
Directorylnfo dinfo =new Directorylnfo ("C:\\Test");
if (dinfo.Exists)
{}
二、创建文件夹
创建文件夹可以使用Directory类的CreateDirectory()方法或者DirectoryInfo类的Create()方法来实现。
1. Directory类的CreateDirectory()方法
CreateDirectory()方法为可重载方法,它有以下两种重载形式。
public static DirectoryInfo CreateDirectory (string path)
public static DirectoryInfo CreateDirectory (string path,DirectorySecurity directorySecurity)
☑ path:要创建的目录路径。
☑ directorySecurity:要应用于此目录的访问控制。
☑ 返回值:第一种重载形式的返回值为由path指定的DirectoryInfo;第二种重载形式的返回值为新创建的目录的DirectoryInfo对象。
当path参数中的目录已经存在或者path的某些部分无效时,将发生异常。path参数指定目录路径,而不是文件路径。
//调用Directory类的CreateDirectory()方法在C盘根目录下创建一个Test文件夹
Directory.CreateDirectory ("C:\\Test ");
2.DirectoryInfo类的Create()方法
Create()方法为可重载方法,它有以下两种重载形式。
public void Create ()
public void Create (DirectorySecurity directorySecurity)
directorySecurity:主要应用于此目录的访问控制。
//调用DirectoryInfo类的Create()方法在C盘根目录下创建一个Test文件夹
Directorylnfo dinfo =new DirectoryInfo ("C:\\Test ");
dinfo.Create();
三、移动文件夹
移动文件夹时,可以使用Directory类的Move()方法或者DirectoryInfo类的MoveTo()方法来实现。
1. Directory类的Move()方法
Move()方法用于将文件或目录及其内容移到新位置,语法如下。
public static void Move (string sourceDirName,string destDirName)
☑ sourceDirName:要移动的文件或目录的路径。
☑ destDirName:指向sourceDirName的新位置的路径。
//调用Directory类的Move()方法将C盘根目录下的Test文件夹移动到C盘根目录下的“新建文件夹”文件夹中
Directory.Move("C:\\Test","C:\\新建文件夹NTest");
使用Move()方法和MoveTo()方法移动文件夹时需要统一磁盘根目录,例如,C盘下的文件夹只能移动到C盘中的某个文件夹下。
2.DirectoryInfo类的MoveTo()方法
MoveTo()方法用于将DirectoryInfo对象及其内容移动到新路径,语法如下。
public void MoveTo (string destDirName)
destDirName:要将此目录移动到的目标位置的名称和路径。目标不能是另一个具有相同名称的磁盘卷或目录,它可以是要将此目录作为子目录添加到其中的一个现有目录。
//调用DirectoryInfo类的MoveTo()方法将C盘根目录下的Test文件夹
//移动到C盘根目录下的“新建文件夹”文件夹中
Directorylnfo dinfo =new Directorylnfo ("C:\\Test ");
dinfo.MoveTo("C:\\新建文件夹\\Test");
//调用DirectoryInfo类的MoveTo()方法将C盘根目录下的Test文件夹 //移动到C盘根目录下的“新建文件夹”文件夹中 Directorylnfo dinfo =new Directorylnfo ("C:\\Test "); dinfo.MoveTo("C:\\新建文件夹\\Test");
四、删除文件夹
删除文件夹可以使用Directory类的Delete()方法或者DirectoryInfo类的Delete()方法来实现。
1. Directory类的Delete()方法
Delete()方法为可重载方法,它有以下两种重载形式。
public static void Delete(string path)
public static void Delete(string path,bool recursive)
☑ path:要移除的空目录/目录的名称。
☑ recursive:若要移除path中的目录、子目录和文件,则为true;否则为false。
//调用Directory类的Delete()方法删除C盘根目录下的Test文件夹
Directory.Delete("C:\\Test");
2.DirectoryInfo类的Delete()方法
Delete()方法是指永久删除文件,语法如下。
public override void Delete()
public void Delete(bool recursive)
recursive:若为true,则删除此目录、其子目录以及所有文件;否则为false。
第一种重载形式,如果DirectoryInfo为空,则删除它;第二种重载形式,删除DirectoryInfo对象,并指定是否要删除子目录和文件。
//调用DirectoryInfo类的Delete()方法删除C盘根目录下的Test文件夹
Directorylnfo dinfo =new Directorylnfo ("C:\\Test");
dinfo.Delete()
五、遍历文件夹
遍历文件夹时,可以分别使用DirectoryInfo类提供的 GetDirectories() 方法、 GetFiles() 方法和
GetFileSystemInfos() 方法。
一般遍历文件夹时,都使用GetFileSystemInfos()方法,因为GetDirectories()方法只遍历文件夹中的子文件夹,GetFiles()方法只遍历文件夹中的文件,而GetFileSystemInfos()方法遍历文件夹中的所有子文件夹及文件。
1.GetDirectories()方法
GetDirectories()方法用来返回当前目录的子目录。该方法为可重载方法,它有以下3种重载形式。
public DirectoryInfo[]GetDirectories()
public Directorylnfo[]GetDirectories(string searchPattern)
public Directorylnfo[]GetDirectories(string searchPattern,SearchOption searchOption)
☑ searchPattern:搜索字符串,如用于搜索所有以单词System开头的目录的“System*”。
☑ searchOption:SearchOption枚举的一个值,指定搜索操作是应仅包含当前目录还是应包含所有子目录。
☑ 返回值:第一种重载形式的返回值为DirectoryInfo对象的数组;第二种和第三种重载形式的返回值为与searchPattern匹配的DirectoryInfo类型的数组。
2.GetFiles()方法
GetFiles()方法用来返回当前目录的文件列表。该方法为可重载方法,它有以下3种重载形式。
public Filelnfo[]GetFiles()
public Filelnfo[]GetFiles(string searchPattern)
public Filelnfo[]GetFiles(string searchPattern,SearchOption searchOption)
☑ searchPattern:搜索字符串(如“*.txt”)。
☑ searchOption:SearchOption枚举的一个值,指定搜索操作是应仅包含当前目录还是应包含所有子目录。
☑ 返回值:FileInfo类型数组。
3.GetFileSystemInfos()方法
GetFileSystemInfos()方法用来返回表示某个目录中所有文件和子目录的FileSystemInfo类型数组。该方法为可重载方法,它有以下两种重载形式。
public FileSystemInfo[]GetFileSystemInfos() public FileSystemInfo[]GetFileSystemInfos(string searchPattern) ☑ searchPattern:搜索字符串。 ☑ 返回值:第一种重载形式的返回值为FileSystemInfo项的数组;第二种重载形式的返回值为与搜索条件匹配的FileSystemInfo对象的数组。
数据结构
数组
数组是用来存储数据的集合
int[] array1 = new int[3];
int[] array2 = new {1, 2, 3};
forecah遍历
foreach (var v in array2)
{
}
动态数组 arraylist
动态数组可以自动调整它的大小
可以使用索引在指定的位置添加和溢移除项目,也允许他在列表中进行动态内存分配。
ArrayList arraylist1 = new ArrayList();
arraylist1.Add(10);
arraylist1.Add(20);
arraylist1.Add(30);
//可用下标访问
int[] array1 = new int{1,2,3,4};
arraylist1.AddRange(array1);
arraylist1.Clear();
//清空数组
arratlist1.Aontains(x);
//返回一个bool值,检查是否包含值x
arraylist1.IndexOf(x);
//返回第一次出现的索引位置,否则返回-1
arraylist1.Inaert(3, 66);
//在第三个索引位置插入66
arraylist1.Remove(12);
//删除第一次出现的目标元素
arraylist1.Reverse();
//反转数组
arraylist1.Sort();
//排序
泛类型集合 List<>
作用与功能类似 srrsylist
无需装箱与拆箱,类型转换,类型安全
List<int> list1 = new List<int>();
list1.Add(1);
list1.Add(2);
//大部分用法和arraylest相同
HashTable
Hashtable 类代表了 基于键的哈代码组织起来的键/值对 集合
用键来访问集合中的元素
Hashtable ht1 = new Hashtable();
ht1.Add("1", 100);
ht1.Add(1, 200);
ht1.Clear();
ht1.ContainsKey("1")
//韩辉一个bool值 查找是否包含指定值
ht1.Count();
//返回有几个键值对
ht1.Remove();
//移除指定键值对
foreach (var k in key)
{
}
//可以使用foreach遍历
字典 Dictionary
泛型容器,存储键值对的数据集合
using System.Collections.Generic;//unity 默认有引用
Dictionary<string, string> dic1 = new Dictionary<string, string>();
dict1.Add("1","100");
dict1.Add("2","200");
dict1.Add("3","300");
dict1.ContainsKey("1")
//判断键存在
dict1["1"] = "1000";
foreach(KeyValuePair<string, string> kvp in dict1)
{
Debug.Log(kvp.Key + " " + kvp.Value);
}
dic1.Remove("2");
//溢出键为2的字典
dic1.Clear();
//清空字典
对于 Hashtable 由于哈希table的键值对使用的是object类型的,所以有装箱拆箱和类型转换的过程,执行效率会低一些
而 字典 在定义时需要指定类型,使用时没有装箱拆箱和类型转换的过程,执行效率很高
HashSet
包含无重复项的无序列表
HashSet<int>hs = new HashSet();
HashSet<int>hs2 = new HashSet();
hs.Add(1);
hs.Add(2);
hs.Add(2);
Debug.Log(hs.Count()); //2
//显示有几个值
hs2.Add(2);
hs2.Add(3);
hs.IntersectWith(hs2);
//得到hs和hs2的 交集 并返回hs
hs.UnionWith(hs2);
//得到hs和hs2的 并集 并返回hs
hs.ExceptWith(hs2);
//得到hs和hs2的 差集 并返回hs(去掉hs中hs2的元素)
hs.SymmetricExceptWith(hs2);
//得到hs和hs2的 对称差集 并返回hs(hs + hs2 - (hs ∩ hs2))
链表
双向链表
LinkedList<int> linList = new LinkedList<int>();
LinkedListNode<int> node;
node = linList.AddFirst(1);
//添加首节点
linList.AddAfter(node, 2);
//在指定节点后添加节点
linList.AddBefore(node, 0);
//在指定节点前添加节点
linkList.Count;//3
//链表长度
linList.First.Value;//0
//首节点的值
linList.Last.Value;//2
//尾节点的值
node.Previous
//节点的前驱
noed.Next
//节点的后驱
堆栈 stack
先进后出的对象集合
Stack st1 = new Stack();
st1.Push("a");
st1.Push("b");
st1.Push("c");
st1.Push("d");
//入栈
String v = (string)st1.Pop();
//出栈
st1.Count();
//栈大小
st1.Peek();
//查看栈顶元素,并不出栈
队列 queue
先进先出
Queue queue = new Queue();
Queue<int> queue2 = new Queue<int>();
queue.Enqueue("1");
queue2.Enqueue(1);
queue2.Enqueue(2);
queue2.Enqueue(3);
int v = queue2.Dequeue();
queue2.clear();
foreach (var val in queue2)
{
}