一.数组
//int[] array;//声明没有分配内存
int[] array = new int[size];//声明并分配内存
array[0]=0;array[1]=1;...array[size]=size;//赋值
int[] array_ = new int[size]{0,1,2...,size};//声明,分配内存并赋值
int[,] array2 = new int[2,3];//二维数组的声明
array2[0,0]=1;//最低下标赋值
array2[1,2]=6;//最高下标赋值
int[,] array2_ = new int[2,3]
{
{1,2,3},
{4,5,6},
};//二维数组声明并赋值
array2.GetLength(0);//得到行数2
array2.GetLength(1);//得到列数3
//三维数组得到三维长度,则array.GetLength(2)
//foreach循环时,假设三维数组[2,2,2],循环下标为[000],[001],[010],[011],[100],[101],[110],[111],规律自己感觉
二.枚举类型意在自定义一个新类型,以特定类型一定的数据量使其编写程序时逻辑上更加方便快捷,且传递参数时让数据更加精确易于判断正误。
enum 枚举类型名{
变量名1,
变量名2,
......,
变量名n,
}
1.string类型对枚举类型的转换:
枚举类型名 变量名 = (枚举类型名)enum.parse(typeof(枚举类型名),"枚举中设定的变量名");
例子:enum Week{
Monday,
Tuesday,
......,
Sunday,
}
Week weekName = (Week)Enum.Parse(typeof(Week),"Tuesday");//""中内容必须是之前枚举类型中定义过的
2.枚举类型对string类型的转换:
//其string值就是定义时的变量名
weekName.toString();
3.枚举类型对int类型的转换:
当枚举类型转换成int类型时,其定义时第一个元素值为整数中的0,第二个元素为1,第三个为2,以此类推。
//继续用上面的例子
int a = (int)weekName;
4.int类型对枚举类型的转换:
//承接上面的例子
weekName = (Week)1;
或者weekName = (Week)Enum.ToObject(typeof(Week),1);
三.类:类的思想是关于数据集合的抽象。生活中,为了方便,我们将一些事物用它们的共同性质加以定义,比如一个苹果,苹果都是可以吃的,酸酸甜甜的,含有一定水分,由树产出等等......在过去,没有类和对象的概念,只有一些基本数据类型,当人们打代码时,过多的基本数据量使人们处理起来更加繁琐,然而里面经常会有很多数据是成批围绕着某个实体概念出现的,为了方便处理过多的数据,人们发明了类的概念,即将一部分数据加以封装,使其成为一个整体,再起个名字,叫XXX类,通过类名创建实例,调用各种数据。如苹果类,通过类名建立一号苹果,重1千克,再建立二号苹果,重500克,然后录入到某个账单中等等。
代码实例:
{
public class Apple(类名)
{
public double weight;//成员变量
public void Development()//成员函数
{
//通过更多的数据量和与其它对象的交互可以将粗略描述改成更精确的描述
Debug.Log("春天发芽,夏天成长,秋天结果,冬天枯萎");
}
}
public Class Behaviour:MonoBehaviour{
void Start()//main()
{
Apple apple1 = new Apple();
apple1.weight = 745.2;
Debug.Log(apple1.weight);
apple1.Development();
}
}
}
- static关键字:在类中用static关键字修饰的变量或函数不再被具体实例对象调用,而转为直接用类名调用。用于做一些类的全局设置或是为方便与其它对象交互暂时写在该类中。称为静态成员变量或静态成员函数。
访问权限修饰符:
public:当用来修饰成员变量和成员函数和类时,所有的类中和程序中都可访问。
private:当用来修饰成员变量和成员函数时,只有本类中可以使用。
internal:修饰成员变量和成员函数和类时,只能在包含它的程序集中访问该项。
protected:修饰成员变量和成员函数时,只在本类中和继承自该类的子类中可以使用。
private和protected不可以用来修饰类。
virtual:表示父类中可被覆盖的函数,子类中覆盖时用override(给编译器看的)。
覆盖指的是子类的函数覆盖了父类的函数,相同的函数名,通过实例化的子类对象只能调用出子类的函数,父类函数被隐藏。
重载指的则是相同的函数名通过给出的参数不同,调用了不同的同名函数。
this:表示使用这个关键字时所在的代码区域的当前类名。
base:表示当前类的父类名。
const:修饰的变量成为常量,被const修饰的变量必须在声明时就初始化,且在某种程度上为不可变值,只能通过类名访问。
readonly:修饰的变量也是常量。但除了声明处还可在构造函数中初始化,且可被类的实例访问, 构造函数和析构函数,类在实例化时,首先加载一次构造函数,通常用于对该对象初始化,当对象消除时,C#自动调用该对象的析构函数,通常有两种情况,一是在该对象的生存周期范围内如函数中或某个大括号括起的代码段中加载完时,自动加载一次析构函数。二是其它使该对象引用消失时,如原本的该对象变量被赋予了新的对象,原对象引用不再能找到。
代码:
{class Apple { //无论实例化多少次都只在第一次实例化时执行一次,且在普通构造函数之前执行 static Apple() { Debug.Log("该类的静态构造函数"); } public Apple(double weight)//构造函数 { this.weight = weight; } ~Apple()//析构函数 { Debug.Log("做一些收尾工作,比如类中特殊成员对象的内存释放"); } public double weight;//成员变量 public void Development()//成员函数 { //通过更多的数据量和与其它对象的交互可以将粗略描述改成更精确的描述 Debug.Log("春天发芽,夏天成长,秋天结果,冬天枯萎"); } }
}
3.类的三大特性:
(1).继承:写法:class 类名2:类名1 称作类名2继承了类名1,意指类名2中可以调用类名1中的所有非private的成员变量和成员函数。C#不支持多继承(一个子类继承两个及以上父类)。
其继承的子类因为可以调用父类的数据,所以子类的数据一定是多于父类的(即便没有任何新增数据,也多了个子类本身的形式上的差异。唯有父类函数被重新改写,且父函数内容多于子函数内容,但这种设计思想并不与一生多的猜想一致),这就保证我们在建立数据模型时,会和我们的智力运作方式一致,即从没有切割的状态到切割的状态。
代码
{class Class1 { public static int x=10; public virtual void Do() { Console.WriteLine("父类行为");//Debug.Log(); } } class Class2:Class1 { public override void D0() { Debug.Log("子类行为"); } } class Program { static void Main(string[] args) { // Class2 class2_1 = new Class2(); Console.WriteLine("Hello World!"+Class2.x); Console.ReadKey(); } }
}
(2).封装:封装就是指使一个类对外功能所需的变量或函数提供接口,而具体实现细节所用到的数据不予提供,用适当的访问修饰符保护起来。一来防止乱改数据程序报错,二来防止数据过多使使用者难以辨别。
代码
{class Dog { private string sound = "汪";//被封装的数据 /* get指该值被使用,set指赋值给该值 public string sound{get;private set;} //这个则指使用sound2的标识符,实则使用sound的值 public string sound2{ get{return sound;} set{sound=value;} } */ public void show(int num)//功能所需的函数 { int i=0; while(i<num) { Console.WriteLine(sound); i++; } } } class Program { static void Main(string[] args) { Dog dog1 = new Dog(); dog1.show(3); Console.ReadKey(); } }
}
(3).多态:指一个父类可以有多个子类。有趣的是,在子类无法继承多个父类的条件下,它保证了继承出的类的层级关系结构一定是树状结构(父类不可再继承子类构成环形),但一旦允许多继承,就将变成图状。
代码
{class Animal { public virtual void show() { } } class Dog:Animal { public override void show() { Console.WriteLine("汪"); } } class Cat:Animal { public override void show() { Console.WriteLine("喵"); } } class Program { static void Main(string[] args) { Animal dog1 = new Dog(); Animal cat1 = new Cat(); dog1.show(); cat1.show(); Console.ReadKey(); } }
}
4.静态类
当static用来修饰类名时,则称该类为静态类。多用于工具类。
(1)仅包含静态成员或常量。
(2)不能被实例化,即不可new+类名()的方式创建。
(3)不能被继承也不能继承Object(官方写好的特殊类,所有类都默认继承,多继承中不包含它)以外的类。
(4)不能包含实例构造函数,但可以包含静态构造函数,静态构造函数在第一次调用该类成员时,优先调用一次该静态类构造函数。
(5)静态构造函数不可被调用
5.抽象类
被abstract修饰的类成为抽象类
(1)不可被new实例化
(2)可以声明abstract修饰的抽象函数,也可以直接实现函数,abstract不能修饰变量。
代码
{abstract class Class1 { static Class1() { Console.WriteLine("父类静态构造函数"); } public static int a=3; public Class1() { Console.WriteLine("父类构造函数"); } public abstract void show();//抽象函数必须被子类覆盖实现 } class Class2:Class1 { static Class2() { Console.WriteLine("子类静态构造函数"); } public Class2() { Console.WriteLine("子类构造函数"); } public override void show() { Console.WriteLine("子类覆盖函数"); } } class Program { static void Main(string[] args) { Class2 c2 = new Class2(); Console.ReadKey(); } } /* 输出结果: 子类静态构造函数 父类静态构造函数 父类构造函数 子类构造函数 */
}
6.密封类
sealed修饰的类称为密封类。密封类一定对应于类的层次树中的叶结点部分。
(1)不可被继承,故而不可以和abstract共同使用,但可以继承其它类。
(2)密封类中不可用sealed修饰方法。当sealed修饰方法时,必须和override一起使用,用在子类覆盖父类的方法上,而后该方法不可被该方法所属类的子类继续覆盖。sealed不可修饰变量。
代码
{class Class1 { public virtual void show() { Console.WriteLine("父类函数"); } public virtual void show2() { Console.WriteLine("父类函数2"); } } class Class2:Class1 { public sealed override void show() { Console.WriteLine("子类密封覆盖函数"); } public override void show2() { Console.WriteLine("子类覆盖函数2"); } } sealed Class3:Class2 { public override void show2() { Console.WriteLine("子类的子类覆盖函数2"); } }
}
7.泛型类
代码:
{class Class1<T> { private T[] array; public Class1(int size) { array = new T[size]; } public void set(int num,T value) { array[num] = value; } public T get(int num) { return array[num]; } } class Program { static void Main(string[] args) { int size=2; Class1<string> c1 = new Class1<string>(size); for(int i=0;i<size;i++) { c1.set(i,"第"+(i+1)+"元素"); } for(int i=0;i<size;i++) { Console.WriteLine(c1.get(i)); } Console.ReadKey(); } }
}
类名+<泛型名>,然后在类里面定义泛型的数据,再在实例化该类的时候指定泛型类型,就可以在某种程度上让一个类同时处理不同数据类型的相同功能。
(1)在实例化时必须指定泛型类型
(2)可以加泛型类型约束,语法为
class 类名<泛型名> where 泛型名:泛型约束
泛型约束必须满足以下条件之一:
I:struct 对于结构约束,T必须是值类型
II:class T必须是引用类型
III:new(),T类型必须要有一个无参的构造函数
IV:NameOfBaseClass,泛型约束必须继承名为NameOfBaseClass
V:NameOfInterface,泛型约束必须实现NameOfInterface的接口
(3)如果子类也是泛型的,那么继承的时候可以不指定具体类型
代码:
{class Class1<T,X> { } class Class2<T,X,Y,A>:Class1<T,X>//泛型类可以直接继承 { } class Class3<Y,A>:Class1<int,string>//泛型类继承时也可以将父类类型指定 { } class Class4:Class1<int,string> { public void show<Y>(Y value)//泛型方法,无需泛型类,只需调用该方法时指定泛型 { Console.WriteLine(value); } } class Program { static void Main(string[] args) { Class4 c4 = new Class4(); c4.show<string>("泛型方法演示"); Console.ReadKey(); } }
}
四.接口
interface+接口名{} 这样的结构被称作一个接口
(1)接口只声明接口函数,不允许实现,且函数必须由public修饰,默认就是public(但是类方法的默认修饰符是private)
(2)接口也可以继承其它接口,且接口不允许被实例化,一个类可以继承多个接口。
接口的设计目的在于被其它类所继承,然后实现其功能。
接口与抽象类的不同:
(1)接口不允许任何被实现的函数,抽象类可以实现函数(接口也不允许构造函数)
(2)抽象类不允许多继承,而接口可以
(3)抽象类可以定义变量,而接口不可以
与抽象类相比,接口更加规范,所以当完全期待由他人来完成某模块的任务时,接口比抽象类更加适合。
代码:
{
interface Interface1
{
void func1();
}
interface Interface2
{
void func2();
}
class Class1:Interface1,Interface2//接口的多继承
{
public void func1()
{
Console.WriteLine("实现接口方法1");
}
public void func2()
{
Console.WriteLine("实现接口方法2");
}
}
class Program
{
static void Main(string[] args)
{
Class1 c1 = new Class1();
c1.func1();
Console.ReadKey();
}
}
}
五.结构体
struct+结构体名{} 称作一个结构体
(1)结构体方法可带有方法,字段,索引,属性,运算符方法和事件。
(2)结构体可以定义有参构造函数,不能定义无参构造函数和析构函数
(3)结构体不能继承其它的类或结构,但是可以实现接口
(4)结构体不能作为其它结构或类的基础结构
(5)结构成员不能指定为abstract,virtual或protected
(6)结构体不用通过new来实例化,但如果调用结构体中的函数则仍需new,但结构体的new并不会在堆上分配内存,仅仅是调用结构体的构造函数,类的new会在堆上分配内存,也会调用类的构造函数。
(7)结构体的变量只能在构造函数中初始化,const常量除外。
代码:
{
struct Struct1
{
public int a;
public const int b=10;
public Struct1(int value)
{
a=value;
}
static Struct1(){}
//当结构体函数要被调用时,还是先要通过new来初始化结构体,但调用变量时只要显示赋值就可以不通过初始化使用。
public void show(){}
}
}
六.委托
如果要把方法作为一个参数来传递的话,就要用到委托。
当将方法引用赋给委托时,应注意方法的返回值类型和参数类型应与委托声明时的类型相一致。如果委托是泛型的,给委托赋值时,委托需注明泛型类型。
delegate+返回值类型+委托名(参数类型 参数名)
例如:delegate void MyDelegate1
代码实例:
{
class Myclass1
{
public void show2()
{
Console.WriteLine("函数2");
}
}
class Program
{
public delegate void Delegate1();
private static void Show1()
{
Console.WriteLine("函数1");
}
static void Main(string[] args)
{
Myclass1 mc1 = new Myclass1();
Delegate1 d1 = mc1.show2;
d1();//输出结果:函数2
d1 = Show1;
d1();//输出结果:函数1
Console.ReadLine();
}
}
}
(1).C#中内置了一个Action委托,该委托无返回值类型,且可用0~16个参数的方法引用进行赋值,使用内置委托Action时,对于赋值方法不同的参数类型需要使用在创建Action委托实例时,就使用泛型声明指定委托实例所能接收的参数类型和参数数量。
代码实例:
{
class Program
{
private static void Show1()
{
Console.WriteLine("函数1");
}
private static void Show2(int num1,int num2)
{
Console.WriteLine("函数2求和:"+num1+"+"+num2+"="+(num1+num2));
}
static void Main(string[] args)
{
Action ac1 = new Action(Show1);
Action<int, int> ac2 = Show2;
ac1();//输出:函数1
ac2(2,3);//输出:函数2求和:2+3=5
Console.ReadLine();
}
}
}
(2).C#中内置了另一个有返回值类型的委托Func,在委托进行泛型声明时,和Action委托唯一的区别时,在声明完参数类型后,需要再声明一种类型作为返回值的类型。如Func<string,int,bool> func1;该委托所接受的方法为带一个string类型和一个int类型的参数,且返回值为bool类型的方法。
代码实例:
{
class Program
{
private static int Show1()
{
return 3;
}
private static string Show2(int x)
{
return "字符串" + x;
}
private static T Show3<T>(T x,int y)
{
Console.WriteLine("" + x + y);
return x;
}
static void Main(string[] args)
{
Func<int> func1 = Show1;
Func<int,string> func2 = Show2;
Func<string, int, string> fun3 = Show3<string>;
Console.WriteLine(func1());//输出:3
Console.WriteLine(func2(2));//输出:字符串2
Console.WriteLine(fun3("泛型x", 3));//输出:泛型x3 泛型x
Console.ReadLine();
}
}
}
(3)匿名方法,即可以不声明方法名而直接赋值给委托调用用的函数
{
class Program
{
static void Main(string[] args)
{
//等价代码
//Action action = ()=>{Console.WriteLine("匿名方法");};
Action action = delegate()
{
Console.WriteLine("匿名方法");
};
action();//输出:匿名方法
Console.ReadLine();
}
}
}
(4)多播委托,委托可以通过+=,-=符号来进行对方法的增减,存储的方法会按存储顺序执行。若其中一个方法出错,后面的方法也不会继续执行。
使用多播委托需注意以下几点:
I.委托属于引用类型,使用委托时要注意委托是否为空值,否则运行可能会出错。
II.若多播委托接收的是带返回值的方法,委托只会返回最后一个方法的返回值。
III.谨慎添加匿名方法,匿名方法因为找不到该方法的引用,在添加后无法从委托中消除,而C#的回收机制也不会将其内存回收。运行期间将会一直占用内存
{
class Program
{
static void Main(string[] args)
{
Func<string> func1 = delegate()
{
Console.WriteLine("默认");
return "默认";
};
func1 += Show1;
func1 += Show2;
if(func1!=null)
{
Console.WriteLine(func1());//输出:默认 show1 show2 show2
}
Console.ReadLine();
}
static string Show1()
{
Console.WriteLine("show1");
return "show1";
}
static string Show2()
{
Console.WriteLine("show2");
return "show2";
}
}
}
(5)事件声明
事件声明需用在声明委托实例时,被声明的委托实例不可以在声明该实例的类外调用,但可以在类外进行方法的添加和删除。其次,被声明的事件委托不可以进行赋值操作,只可以进行+=,-=操作,用来添加和删除方法,同样需注意多播委托的注意事项。声明事件的主要作用在于对委托实例进行封装,虽然不可以直接调用,但是可以通过该事件实例所属类的其它方法进行事件委托的调用,作为其他类使用该事件的出口。
{
class MyClass
{
public delegate void Delegate1();
public event Delegate1 delegate1;
public void useDelegate1()
{
delegate1();
}
}
class Program
{
static void Main(string[] args)
{
MyClass mc1 = new MyClass();
mc1.delegate1 += () => { Console.WriteLine("事件初始化"); };
//mc1.delegate1();不合法
mc1.useDelegate1();//输出:事件初始化
Console.ReadLine();
}
}
}