简介 本文记录c#学习,仅供个人参考。
基本语法 控制台方法相关 使用命名空间System中的console类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 using System;namespace Project { internalk class Program {static void Main (string [] args ) { Console.WriteLine(); Console.Write(); Console.ReadLine(); Console.ReadKey(); Console.Readkey(true ).keychar Console.Clear(); Console.SetWindowSize(width,height); Console.SetBufferSize(); Console.SetCursorPosition(left,top); Console.ForegroundColor=ConsoleColor.red; Console.BackgroundColor=ConsoleColor.white; Console.CursorVisible = false ; Environment.Exit(0 ); } } }
变量相关 输入#region,再按tab键自动补全,可以将#region和#endregion之间的代码折叠起来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 using System{ namespace Project { class Program { static void Main (string [] args ) { #region myregion #endregion } } } }
变量的区别:float浮点数申请需要末尾加一个f,不加的话默认是double类型,float有效数字保留8位,从第一个非零数字开始数起,decimal能存储25~27位的有效数字,但不建议使用,末尾加m;c#中char类型占两个字节,能用汉字赋初值,但部分生僻字不能。
类型转换 隐式和显式转换和c语言,c++没多大差别。字符串转换:类型.Parse(“字符串”);(比如说int.Parse(“123”)等于整型123)。
Convert.To类型名();转换精度更高。其他类型转string:变量类型.toString();
1 2 3 4 5 6 7 8 string str=1. toString();str=true .toString(); Console.WriteLine("12354" +1 +true ); int a=int .Parse("123" );char b=char .Parse("A" );
异常捕获 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 try { } catch { } finally { }
字符串拼接 直接将字符串+字符串就能实现拼接。
或者使用string.Format(“带拼接的内容”,内容1,内容2…..);(有点类似于格式化输出)
1 2 string .Format("{0}を愛するみんなさん、{1}んにちは!、ハメドリ君だ{2}" ,"ドスケベセックス" ,"シコ" ,"ハメ" );
位运算 位与(&):连接两个数值进行位运算,将数值转为二进制。对位运算,有零为零。
位或(|):连接两个数值进行位运算,将数值转为二进制。对位运算,有一为一。
异或(^):连接两个数值进行位运算,将数值转为二进制。对位运算,相同为零,不同为一。
位取反(~):写在数值前面,零变一,一变零。
左移(<<)和右移(>>):让一个二进制的数左移或右移。
左移几位,右边多加几个零。
右移几位,右侧去掉几个数。
随机数 1 2 Random r=new Random(); int i=r.Next(a,b);
复杂数据类型 枚举 被命名的整型常量的集合,一般用它来表示状态,类型等等。通常和switch语句搭配使用
声明枚举:创建一个自定义的枚举类型。(枚举可以声明在namespace语句块中,也可以声明在class和struct语句中,不能在函数语句块中声明)
声明枚举变量:使用自定义的枚举类型,创建一个枚举变量。
类型转换用强制转换。用tosString();转成枚举项的名字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 enum E_自定义枚举名{ 自定义枚举项名字, 自定义枚举项名字1 , ... 自定义枚举项名字n } eg: enum E_player{ main, other } E_player playertype=E_player.mian; playertype=(E_player)Enum.Parse(typeof (E_player),"枚举项名称" );
数组 语法:变量类型 []变量名=new 变量类型[数组大小]{这里可以赋值};(默认初始化为零)//一维数组
变量类型[,]变量名=new 变量类型[行数,列数];//二维数组
1 2 3 4 5 const int N=100 ;int [] a=new int [N]{此处可以赋值};int [,] a=new int [3 ,3 ]{此处可以赋值};Console.WriteLine(a.GetLength(0 ); Console.WriteLine(a.GetLength(1 );
交错数组 语法:变量类型 [] []变量名=new 变量类型[] []
1 2 3 int [][]arr=new int[3][]{new int[3]{1,2,3},new int[2]{1,2},new int[1]{1}}; int [][]arr=new int[][]{new int[3]{1,2,3},new int[2]{1,2},new int[1]{1}}; int [][]arr={new int[3]{1,2,3},new int[2]{1,2},new int[1]{1}}
值类型与引用类型 引用类型:string,数组,类
值类型:其它,结构体
引用类型和值类型存储的内存区域不相同,存储方式也不同。
值类型存储在栈上,系统分配,自动回收,小而快。
引用类型存储在堆空间,手动申请和释放,大而慢。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 using System.Diagnostics.CodeAnalysis;using System.Runtime.InteropServices;namespace 测试程序{ class Project { static void Main (string [] args ) { int a = 10 ; int b = a; int [] arr = new int [] { 1 , 2 , 3 , 4 }; int [] arr2 = arr; Console.WriteLine("a的值为{0},b的值为{1}" ,a,b); Console.WriteLine("arr[0]的值为{0},arr2[0]的值为{1}" ,arr[0 ], arr2[0 ]); b = 20 ; arr2[0 ] = 9 ; Console.WriteLine("修改之后的值为:" ); Console.WriteLine("a的值为{0},b的值为{1}" , a, b); Console.WriteLine("arr[0]的值为{0},arr2[0]的值为{1}" , arr[0 ], arr2[0 ]); } } }
string类型 虽然是引用类型,却不遵循引用类型的赋值方式,一般情况下引用类型赋值会把地址赋值给新变量,修改变量值时会直接修改地址内存内存储的值,但是string类型在赋值时会新开辟一段内存存储,使变量名指向新的地址,和值类型类似。
ref和out 可以在函数内部改变外部传入的内容,类似于c++中的引用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 using System.Collections.Specialized;using System.ComponentModel.Design;using System.Diagnostics.CodeAnalysis;using System.Reflection.Metadata.Ecma335;using System.Runtime.InteropServices;namespace 测试程序{ class Project { static void changevaule (ref int a ) { a = 3 ; } static void changearr (ref int []arr ) { arr = new int [] { 10 ,20 ,30 }; } static void Main (string [] args ) { int [] arr = new int [] { 1 ,2 ,3 }; Console.WriteLine(arr[1 ]); changearr(ref arr); Console.WriteLine(arr[1 ]); } } }
变长参数和参数默认值 变长参数关键字:params
变长参数只能是函数的最后一个参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 namespace 测试程序{ class Project { static int Sum (params int []arr ) { int sum = 0 ; for (int i=0 ;i<arr.Length;i++) { sum += arr[i]; } return sum; } static int speak (string str="hello" ) { Console.WriteLine(str); } static void Main (string [] args ) { int ans = Sum(1 ,2 ,3 ,4 ,5 ,6 ,7 ); Console.WriteLine(ans); speak(); } } }
结构体 构造函数:函数名与结构体名相同,没有返回值,this关键字使用this.和class不相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 student s1=new student( namespace 测试程序 { class Project { struct student { int age; string name; bool sex; public student () { age = 114514 ; name = "田鼠浩二" ; sex = true ; } public void speak () { Console.WriteLine("我叫{0},我是{1}性,今年{2}岁" ,name,sex?'男' :'女' ,age); } }; static void Main (string [] args ) { student s1=new student(); s1.speak(); } } }
面向对象 三大特性:封装、继承、多态
封装 类和对象 类一般声明在namespace语句块中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class type { } class Person { } Person p; Person p2=null ; Person p3=new Person();
成员变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 enum E_Animal{ dog, cat, bird } class Pet { string name; E_Animal type; } enum E_SexType{ Man, Woman } class Person { string name="田鼠浩二" ; int age; E_SexType sex; Person grilFriend; Person[] boyfriend; Pet pet=new Pet(); } default (类型名);
成员方法 别加static关键字
用法跟函数类似
构造、析构、垃圾回收 构造函数:在实例化对象时,会调用的用于初始化的函数,如果不写,默认存在一个无参构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Person { public string name; public int age; public Person () { name="田鼠浩二" ; age=114514 ; } public Person (string name,int age ) { this .name=name; this .age=age; } public Person (string name,int age ):this () { } public Person (string name ) { } public Person (string name,int age ):this (name ) { } }
析构函数:但引用类型的堆内存被回收时,会调用该函数
//对于需要手动管理内存的语言(比如c++),需要在析构函数中做一些内存回收处理,但是c#中存在自动垃圾回收机制GC,所以几乎不会用到构造函数,在unity开发中几乎不会用到
语法:~类名()
{
}
垃圾回收机制 先放一边,之后再补
成员属性 基本概念:用于保护成员变量,为成员属性的获取和复制添加逻辑处理,解决3p的局限性,属性可以让成员变量在外部只能获取,不能修改或者是只能修改不能获取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 get {}set {}class Person (){ private string name; private int age; private int money; private bool sex; public string Name { get { return name; } set { name=value ; } } public int Money { get { return money-5 ; } set { money=value +5 ; } } public float Height { get ; private set ; } } Person p=new Person(); p.Name="田鼠浩二" ; Console.Write(p.Name);
索引器 基本概念:让对象想数组一样通过索引访问其中元素,是程序看起来更直观,更容易编写(就是专门用来访问类中数组的,类似于cpp中的运算符重载),和函数写法类似,只是把括号改成中括号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Person { private strint name; private int age; private Person[] friends; public Person this [int index] { get { return friends[index]; } set { friends[index]=value ; } } } Person p=new Person(); p[0 ]=new Person(); Console.Write(p[0 ]);
静态成员 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class Test { public static float PI=3.1415926f ; public testInt=100 ; public static float CalcCircle (float r ) { return PI*r*r; } public void TestFun () { Console.Write("123" ); } } Console.Write(Test.PI);
静态类和静态构造函数(工具类) 用static修饰的类,特点:只能包含静态成员,不能被实例化。作用:将常用的静态成员写在静态类中,方便使用,静态类不能被实例化,体现了工具类的唯一性,比如Console就是一个静态类。
静态构造函数:特点:静态类和普通类都可以有,不能使用访问修饰符,不能有参数,只会调用一次
作用:在静态构造函数中初始化 静态变量
使用:静态类中的静态构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 static class TestStatic { public static int testIndex=0 ; public static void TestFun () { } public static int TestIndex { get ; set ; } } static class StaticClass { public static int testInt = 100 ; public static int testInt2 = 200 ; static StaticClass () { Console.WriteLine("静态构造函数" ); } } class Test { public static int testInt=200 ; static Test () { Console.WriteLine("静态构造" ); } public Test () { Console.WriteLine("普通构造" ); } }
拓展方法 基本概念:为现有的 非静态 变量类型 添加新方法,作用:提升程序拓展性,不需要在对象中重新写方法,不需要继承来添加方法,为别人封装的类型写额外的方法。
特点:一定是写在静态类中,一定是一个静态函数,第一个参数为拓展目标,第一个参数用this修饰
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 static class Tools { public static void SpeakValue (this int value ) { Console.WriteLine("田鼠浩二为int拓展的方法" +value ); } public static Fun3 (this Test t ) { Console.WriteLine("为Test拓展的方法" ); } } int i=10 ;i.SpeakValue(); class Test { public int i=10 ; public void Fun1 () { Console.Write("123" ); } public void Fun2 () { Console.WriteLine("456" ); } } Test t=new Test(); t.Fun3();
运算符重载 概念:让自定义类和结构体能够使用运算符
使用关键字 operator
特点:一定是一个公共的静态方法,返回值卸载operator前,逻辑处理自定义
作用:让自定义类和结构体对象可以进行运算,条件运算符需要成对实现,一个符号可以多个重载,不能使用ref和out(相当于自定义一种运算方式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public static 返回类型 operator 运算符(参数列表) class Point { public int x; public int y; public static Point operator +(Point p1,Point p2) { Point p=new Point(); p.x=p1.x+p2.x; p.y=p1.y+p2.y; return p; } public static Point operator +(Point p1,int value ) { Point p=new Point(); p.x=p1.x+value ; p.y=p1.y+value ; return p; } } Point p1=new Point(); p1.x=1 ; p1.y=1 ; Point p2=new Point(); p2.x=2 ; p2.y=2 ; Point p3=p1+p2; Point p4=p3+2 ;
可重载的运算符:算术运算符,逻辑运算符(只有非!),位运算符,条件运算符(需要成对实现,重载了大于就要重载小于)
不可重载的运算符:逻辑与,逻辑或,索引符,强转运算符,特殊运算符,点,三目运算符,赋值符号
内部类和分布类 内部类:一个类中再申明一个类
特点:使用时要用包裹者点出自己
作用:亲密关系的变现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Person { public int age; public string name; public Body body; public class Body { Arm leftArm; Arm rightArm; class Arm { } } } Person p=new Person(); Person.Body body=new Person.Body(); Person.Arm
分布类:把一个类分成几部分申明
关键字:partial
作用:分布描述一个类,增加程序的拓展性。(可以写在多个脚本文件中,分布类的访问修饰符要一致,不能有重复成员)其实就是把一个类分成好几个语句块中写。
分部方法:将方法的申明和实现分离
特点:不能加访问修饰符(默认私有),只能在分布类中申明,返回值只能是void,可以有参数但不用,out关键字,局限性大,了解即可。
继承 继承的基本概念 基本概念:一个类a继承一个类b,类a会继承类b的所有成员,a类将拥有类b的所有特征和行为。
被继承的类叫做父类、基类、超类。继承的类叫做子类、派生类。子类可以有自己的特征和行为。
特点:单根性 子类只能有一个父亲。传递性 子类可以间接继承父类的父亲(类似于树状结构)
c#中允许父类和子类同名的成员,但不建议使用(会覆盖父类的同名成员)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Teacher { public string name; protected int number; public void SpeakName () { Console.WriteLine(name); } } Class TeachingTeacher:Teacher { public string subject; public void SpeakSubject () { Console.WriteLine(subject+"老师" ); } } TeachingTeacher tt=new TeachingTeacher(); tt.name="田鼠浩二" ; tt.number=1 ; tt.SpeakName(); tt.subject="unity" ; tt.SpeakSubject();
里氏替换原则(LSP)(面向对象七大原则之一) 基本概念:任何父类出现的地方,子类都可以替代。父类容器装子类对象,因为子类包含了父类的全部内容
作用:方便进行对象存储和管理
is:判断一个对象是否是指定类对象,返回值是bool,是为真,不是为假。
as:将一个对象转换为指定类对象,返回值为指定类对象,失败返回null
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class GameObject { } class Player : GameObject { public void PlayerAtk () { Console.WriteLine("玩家攻击" ); } } class Monster : GameObject { public void MonsterAtk () { Console.WriteLine("怪物攻击" ); } } class Boss : GameObject { public void BossAtk () { Console.WriteLine("Boss攻击" ); } } GameObject player = new Player(); GameObject monster = new Monster(); GameObject boss = new Boss(); GameObject[] objects = new GameObject[] { new Player,new Monster,new Boss}; if (player is Player){ } Player p=player as Player; Player p=monster as Player;
继承中的构造函数 特点:当申明一个子类对象时,先执行父类的构造函数,再执行子类的构造函数
父类的无参构造函数很重要。子类可以通过base关键字代表父类,调用父类构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Father { public Father () { } public Father (int i ) { Console.WriteLine("Father构造" ); } } class Son :Father { public Son (int i ):base (i ) { } public Son (int i,string str ):this (i ) { } }
万物之父和装箱拆箱 关键字:object,概念:是所有类型的父类,是一个类(引用类型),作用:可以利用里氏替换原则,用object容器装所有对象,可以用来表示不确定类型,作为函数参数类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 class Father { } class Son :Father { public void Speak () { } } Father f=new Son(); if (f is Son){ (f as Son).Speak(); } object o=new Son();if (o is Son){ (o as Son).Speak(); } object o2=1f ;float f1=(float )o2;object str="123123" ;string str2=str as string ;object arr=new int [10 ];int []ar=arr as int [];object v=3 ;int inValue=(int )v;static void TestFun (params int [] array ){ }
密封类(不是很重要) 概念:使用sealed密封关键字修饰的类
作用:让类无法再被继承
1 2 3 4 5 6 7 8 9 10 11 12 class Father { } sealed class Son :Father { }
多态 多态vob 多态:多种状态,让继承同一父类的子类们,在执行相同方法时有不同的表现,解决的问题:让同一个对象有唯一行为的特征。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 class Father { public void SpeakName () { } } class Son :Father { public new void SpeakName () { } } Father f=new Son(); f.SpeakName(); (f as Son).SpeakName(); class GameObject { public string name; public GameObject (string name ) { this .name=name; } public virtual void Atk () { Console.WriteLine("游戏对象进行攻击" ); } } class Player :GameObject { public Player (string name ):base (name ) { } public override void Atk () { base .Atk(); console.WriteLine("让玩家对象进行攻击" ); } }
即使使用了父类容器装载子类的实例化对象,但是通过重写之后可以直接使用子类的同名函数,不需要通过as进行转化了
抽象类和抽象方法 抽象类
概念:被抽象关键字abstract修饰的类,特点:不能被实例化,可以包含抽象方法,继承抽象类必须重写其抽象方法
抽象函数
又叫纯虚方法,用abstract修饰的方法,特点:只能在抽象类中申明,没有方法体,不能是私有的,继承后必须要实现,用override重写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 abstract class Thing { public string name; } class Water :Thing { } Thing t=new Water(); abstract class Fruits { public string name; public abstract void Bad () ; public virtual void Test () { } } class Apple :Fruits { public override void Bad () { } public override void Test () { } } class SuperApple :Apple { public override void Bad () { } public override void Test () { } }
接口 概念:是行为的抽象规范,是一种自定义类型,关键字:interface
接口申明的规范:不包含成员变量,只包含方法、属性、索引器、事件,成员不能被实现,成员可以不用写访问修饰符,不能是私有的,接口不能继承类,但是可以继承另一个接口
接口的使用规范:类可以继承多个接口,类继承接口后,必许实现接口中的所有成员
特点:和类的声明类似,接口是用来继承的,接口不能被实例化,但是可以作为容器存储对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 interface IFly { void Fly () ; string Nmae { get ; set ; } int this [int index] { get ; set ; } event Action doSomething { } } class Animal { } class Person :Animal ,IFly { public virtual void Fly () { } public string Name { get ; set ; } public int this [int index] { get { return 0 ; } set { } } public event Action doSomething; } IFly f=new Person();
接口的主要作用:类似于行为的多态,可以为不同种对象但是又有相同行为的对象提供方法
接口可以继承接口,不需要去实现,待类继承接口后,类自己去实现所有内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 interface IAtk { void Atk () ; } interface ISuperAtk { void Atk () ; } class Player :IAtk ,ISuperAtk { public void IAtk.Atk() { } public void ISuperAtk () { } }
抽象类和接口的区别 抽象类和抽象方法:abstract修饰的类和方法,抽象类不能实例化,抽象方法只能在抽象类中申明,是纯虚方法,必须在子类中实现。
接口:interface 自定义类型,是行为的抽象,不包含成员变量,仅包含方法,属性,索引器,事件,成员都不能是实现,建议不写访问修饰符,默认public
相同点:都可以被继承,都不能直接实例化,都可以包含方法申明,子类必须实现未实现的方法,都遵循里氏替换原则。
不同点:抽象类中可以有构造函数,接口中不能,抽象类只能被单一继承,接口可以被继承多个,抽象类中可以有成员变量,接口中不能,抽象类中可以申明成员方法,虚方法,抽象方法,静态方法;接口只能申明没有实现的抽象方法,抽象类方法可以使用访问修饰符,接口中建议不写,默认public
使用:表示对象的用抽象类,表示行为拓展的用接口,不同对象拥有的共同行为,我们往往可以使用接口来实现
密封函数(不是很重要) 概念:用密封关键字sealed修饰的重写函数,作用:让虚方法或者抽象方法之后不能再被重写,特点:和override一起出现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 abstract class Animal { public string name; public abstract void Eat () ; public virtual void Speak () { Console.WriteLine("叫" ); } } class WhitePerson :Person { public override void Eat () { base .Eat(); } public override void Speak () { base .Speak(); } } class Person :Animal { public sealed override void Eat () { } public override void Speak () { } }
命名空间 概念:命名空间是用来组织和重用代码的,作用:就像是一个工具包,类就像是一件件的工具,都是申明在命名空间里的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 namespace 命名空间名{ } namespace MyGame { class GameObject { } } namespace MyGame { class Player :GameObject { } } using 命名空间名
万物之父中的方法 object中的静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Test { } Test t=new Test(); Test t2=new Test(); Object.Equals(t,t2); Object.referenceEquals(t2,t)
object中的成员方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 Test t=new Test(); Type type =t.GetType(); class Test { public int i=1 ; public Test2 t2=new Test(); public Test clone () { return MemberwiseClone() as Test; } } class Test2 { public int i=2 ; } Test t2=t.Clone(); Console.WriteLine(t.i); Console.WriteLine(t.t2.i); Console.WriteLine(t2.i); Console.WriteLine(t2.t2.i); t2.i=20 ; t2.t2.i=21 ;
object中的虚方法
string类型 字符串本质是一个char数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 string str="田鼠浩二" ;Console.WriteLine(str[0 ]); char []chars=str.ToCharArray();Console.WriteLine(chars[1 ]); for (int i=0 ;i<str.Length;i++){ Console.WriteLine(str[i]); } str=string .Format("{0}{1}" ,1 ,3333 ); console.WriteLine(str); str="我是田鼠浩二" ; str.indexOf("田" ); int index=str.IndexOf("吊" );Console.WriteLine(index); str="我是田鼠浩二田鼠浩二" ; index=str.LastIndexOf("田鼠浩二" ); str="我是田鼠浩二田鼠浩二" ; str=str.Remove(4 ); Console.WriteLine(str); str=str.Remove(1 ,1 ); str="我是田鼠浩二田鼠浩二" ; str=str.Replace("田鼠浩二" ,"田所浩二" ); Console.WriteLine(str); str="naohdflkdsafs" ; str=str.ToUpper(); Console.WriteLine(str); str=str.ToLower(); Console.WriteLine(str); str="田鼠浩二" ; str.Substring(2 ); Console.WriteLine(str); str=str.Substring(2 ,3 ); Console.WriteLine(str); str=str.Substring(0 ,1 ); Console.WriteLine(str); str="1,2,3,4,5,6,7,8" ; string []strs=str.Split(',' );for (int i=0 ;i<strs.Length;i++){ Console.WriteLine(strs[i]); }
StringBuilder 优势是可以预先分配内存,避免过度浪费,其他和string差不多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 StringBuilder str=new StringBuilder("123123123" ); Console.WriteLine(str); Console.WriteLine(str.Capacity); Console.writeLine(str.Length); str.Append("3333" ); str.AppendFormat("{0}{1}" ,100 ,200 ); str.Insert(0 ,"田鼠浩二" ); str.Remove(0 ,10 ); str.Clear(); Console.WriteLine(str[0 )]; str[0 ]='A' ; str.Replace("1" ,"田" ); str.Clear(); str.Append("123" ); str.Equals("123" )
类和结构体的区别 类是引用类型,结构体是值类型。都可以用来形容对象,结构体没有继承和多态的特性,结构体不具备继承的特性,不能用protected保护访问修饰符。结构体成员变量不能指定初始值,结构体不能申明无参的构造函数,结构体申明有参构造函数后,无参的不会被顶掉,结构体不能申明析构函数,结构体不能被静态static修饰,类可以,结构体不能在自己内部申明和自己一样的结构体变量,类可以。
结构体可以继承接口,因为接口是行为的抽象。
选择:想要用继承和多态是直接用类(比如玩家,怪物等),对象是数据集合时优先考虑结构体(比如位置,坐标等)
从值类型和引用类型赋值时的区别考虑,比如经常被赋值传递的对象,并且改变赋值对象,原对象不想跟着变化时,用结构体,比如坐标,向量,旋转等。
多脚本文件 新建脚本文件:
一般一个类一个脚本,一个结构体一个脚本,一个接口一个脚本,枚举在特殊情况下也可以一个脚本。
在文件夹中新建脚本文件:
可以将相同类型的脚本放在一个文件夹中
c#脚本文件后缀是.cs c#解决方案中的一些重要文件夹 如bin 解决方案资源管理器窗口 工程右键 点添加
文件中新建脚本文件需要注意命名空间改变
UML类图 统一建模语言(Unified Modeling Language,UML)是一种为面向对象系统的产品进行说明、可视化和编制文档的一种标准语言,是非专利的第三代建模和规约语言。UML是面向对象设计的建模工具,独立于任何程序设计语言。
使用一些高级的UML可视化软件,不用写代码,通过做一些图表相关内容就可以直接生成代码,在其基础上进行开发。他的最终目标是直接能通过图形就把业务逻辑完成
UML类图是UML其中很小的一部分,可以帮助我们理清对象间关系
面向对象七大原则 单一职责原则(SRP) 类被修改的几率很大,因此应该专注于单一的功能。如果把多个功能放在同一个类中,功能之间就形成了关联,改变其中一个功能,可能终止另一个功能。例如程序,策划,美术三个工种是三个类,应该各司其职,只做自己的工作
开闭原则(OCP) 对拓展开发,对修改关闭。拓展开放:模块的行为可以被拓展从而满足新的需求 拓展关闭:不允许修改模块的源代码(或者尽量使修改最小化)继承就是最典型的开闭原则的体现,可以通过添加子类和重写父类的方法来实现。
里氏替换原则(LSP) 任何父类出现的地方,子类都可以替代。
依赖倒转原则(DIP) 要依赖与抽象,不要依赖于具体的实现。
迪米特原则(LoP) 最少知识原则,一个对象应当对其它对象尽可能少的了解,不要和陌生人说话。
一个对象的成员,要尽可能少的直接和其他类建立关系,降低耦合性
接口分离原则(ISP) 不应该强迫别人依赖他们不需要使用的方法,一个接口不需要提供太多的行为,一个接口应该尽量只提供一个对外的功能,让别人去选择需要实现什么样的行为,而不是把所有行为封装到一个接口里。
合成复用原则(CRP) 尽量使用对象组合,而不是继承来达到复用的目的,继承关系是强耦合,组合关系是低耦合,例如脸是眼睛、鼻子、嘴巴、耳朵的组合,而不是继承,角色和装备也是组合。
数据结构 ArrayList 本质是一个object类型的数组,在system.collections命名空间里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 ArrayList array=new ArrayList(); array.Add(); ArrayList array2=new ArrayList(); array.AddRange(array2); array.Insert(1 ,"1234565" ); array.Remove(); array.RemoveAt(2 ); array[0 ]; arr.Contains() array.IndexOf(); array.LastIndexOf(); array[0 ]="000" ; array.Count array.capacity for (int i=0 ;i<array,Count;i++ ){ Console.WriteLine(array[i]); } foreach (object item in array){ Console.WriteLine(item); } int i=1 ;array[0 ]=i; i=(int )array[0 ];
Stack 栈,先进后出,本质也是一个object数组,只不过封装了特殊的存储规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 Stack stack=new Stack(); stack.Push(); stack.Pop(); stack.Peek(); stack.Contains(); stack.Clear(); stack.Count foreach (object temp in stack){ Console.WriteLine(temp); } object []array=stack.ToArray();for (int i=0 ;i<array.Length;i++){ } while (stack.Count>0 ){ object o=stack.Pop(); Console.WriteLine(o); }
Queue 队列,先进先出,本质也是一个object数组,只不过封装了特殊的存储规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Queue queue=new Queue(); queue.Enqueue(); queue.Dequeue(); queue.Peek(); if (queue.Contains()){ } queue.Clear(); queue.Count; foreach (object item in queue){ }
HashTable 哈希表,也就是散列表,使用键来访问集合中的元素,就是c++中的map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 Hashtable hashtable=new Hashtable(); hashtable.Add(1 ,"123" ); hashtable.Add("123" ,2 ); hashtable.Add(true ,true ); hashtable.Remove(); hashtable.Clear(); hashtable[键]; hashtable.Contain(键); hashtable.ContainKey(键); hashtable.ContainValue(值); hashtable.Count; foreach (object item in hashtable.Keys){ Console.WriteLine(item); Console.writeLine(hashtable[item]); } foreach (object item in hashtable.Values){ Console.WriteLine(item); } foreach (DictionaryEntry object item in hashtable){ Console.WriteLine(item.key,item.Value); } IDictionaryEnumerator myEnumerator= hashtable.GetEumerator(); bool flag=myEnumerator.MoveNext();while (flag){ Console.WriteLine(myEnumerator.Key,myEnumerator,Value); }
泛型 概念:实现了类型参数化,达到代码重用的目的,相当于类型占位符,定义类或者方法时使用替代符代表变量类型,当真正使用类或者方法时再具体指定类型。(c++中vector类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class 类名<泛型占位字母>interface 接口名<泛型占位字母>函数名<泛型占位字母>(参数列表) class TestClass <T >{ public T value ; } TestClass<int > t=new TestClass<int >(); t.value =10 ; Console.WriteLine(t.value ); TestClass<string >t2=new TestClass<string >();
泛型约束 概念:让泛型有一定的限制,关键字:where
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 class Test1 <T >where T :struct { public T value ; public void TextFun <K >(K,k )where K:struct { } } class Test2 <T >where T :class { public T value ; public void TestFun <K >(K,v ) where K:class public Test2 (int a ) { } } class Test3 <T >where T :new (){ public T value ; public void TestFun (K,k )where K:new () { } } void main (){ Test1<object >t1=new Test1<object >(); Test1<int >t2=new Test2<int >(); Test3<Test1>t3=new Test3<Test1>(); Test3<Test2>t4=new Test3<Test2>(); }
泛型数据结构类 List 是c#中封装好的一个类,是可变类型的泛型数组,List帮助我们实现了许多方法,类似于cpp中的vector
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 List<int >list=new List<int >(); List<string >list2=new List<bool >(); list.Add(); list.Remove(); list.RemoveAt(); list[0 ]; list.IndexOf(); list.LastIndexOf(); list.Insert(); list.Count; list.Capacity; foreach (int temp in list){ Console.WriteLine(temp); }
Dictionary 字典,有泛型的hashtable,就是hashtable,尽量用这个,别用非泛型类型,这个类型和hashtable的使用方法是一样的,这里笔者不多说了,和hashtable区别:找不到键的话直接报错,用TryGetValue方法不会报错
LinkedList 封装好的类,是一个泛型的双向链表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 LinkedList<int >linkedlist=new LinkedList<int >(); linkedlist.AddLast(); linkedlist.AddFirst(); LinkedListNode<int >n=linkedlist.Find(20 ); linkedlist.AddAfter(n,12 ); AddBefore() linkedlist.RemoveFirst(); linkedlist.RemoveLast(); linkedlist.Remove(具体元素); linkedlist.Clear(); LinkedListNode<int >first=linkedlist.First; LinkedListNode<int >last=linkedlist.Last; LinkedListNode<int >node=linkedlist.Find(具体元素); linkedlist.Contains(元素); node.value =新值; foreach (int item in linkedlist){ Console.WriteLine(item); }
泛型栈和队列 和c++中的一样
委托 概念:委托是函数的容器,可以理解为表示函数的变量类型,用来存储传递函数,委托的本质是一个类,用来定义函数的类型,不同的函数必须对应和各自“格式”一致的委托。(类似于函数指针),可以作为某些触发机制,在前置条件完成后,将委托中的函数(行为)全部执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 delegate void MyFun () ;delegate int MyFun2 (int a ) ;static void Fun () { Console.WriteLine("你好" ); } static int Fun2 (int value ){ return value ; } Main() { MyFun f=new MyFun(Fun); f,Invoke(); MyFun f2=Fun; f2(); Test t=new Test(); t.TestFun(Fun,Fun2); } class Test { public MyFun fun; public MyFun2 fun2; public void TestFun (MyFun fun,MyFun fun2 ) { int i=1 ; i+=2 ; i+=2 ; fun(); fun2(i); this .fun=fun; this .fun2=fun2; } public void RemoveFun (MyFun fun,MyFun2 fun2 ) { this .fun-=fun; this .fun2-=fun2; } } MyFun ff=null ; ff+=Fun; ff+=Fun; ff(); ff=null ; if (ff!=null ){ ff(); }
系统定义好的委托(使用系统自带的委托需要引用system):
1 2 3 4 public delegate void Action () ;public delegate TResult Func <out TResult >() ;public delegate void Action <in T >(T obj ) ;public delegate TResult Func <in T ,out TResult >(T arg ) ;
回调机制(补充) 回调是一种编程模式,其中一个函数作为参数传递给另一个函数,并在特定条件满足或者事件发生时调用。
一般可以使用委托实现。因为在回调中通常只存在一个订阅者,所以强调的是完成时调用我的单向通知,
调用者是知道被调用者的。
观察者模式(补充) 观察者模式是一种行为设计模式,定义了对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象多会自动收到通知并更新。一般可以使用事件实现。因为观察者模式中通常有多个订阅者,强调“‘某事发生了”的广播通知,让多个订阅者知道事件的发生
事件 事件是基于委托的存在,事件是委托的安全包裹,让委托的使用更具有安全性,事件是一种特殊的变量类型,是特殊封装的委托。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 class Test { public Action myFun; public event Action myEvent; public Test () { myFun=TestFun; myEvent=TestFun; myEvent+=TestFun; } public void TestFun () { } } class Project { static void Main (string [] args ) { Test t = new Test(); t.myFun = null ; t.myEvent = null ; t.myFun(); t.myEvent(); } }
空条件运算符(补充) 在调用委托和事件时通常要判断是否为空,具体可写一个条件语句判断,这里可以用更加简洁的空条件运算符(?.)
1 2 3 4 5 6 if (event !=null ){ event (); } event ?.();
匿名函数 概念:没有名字的函数,主要是配合委托和事件进行使用,脱离委托和事件是不会使用匿名函数的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 Action a = delegate () { Console.WriteLine("匿名函数逻辑" ); }; a(); Action<int ,string > b = delegate (int a,string b) { Console.WriteLine(a); Console.WriteLine(b); }; b(100 ,"123" ); Func<string > c= delegate () { return "12345" ; } Console.WriteLine(c()); Test t=new Test(); t.Dosomething(100 ,delegate (){ }); t.GetFun()(); class Test { public Action action; public void Dosomething (int a,Action fun ) { Console.WriteLine(a); fun(); } public Action GetFun () { return delegate () { Console.WriteLine("函数内部返回的一个匿名函数逻辑" ); }; } }
lambda表达式 可以将lambda表达式理解为匿名函数的简写,他除了写法不同外,使用上和你匿名函数一样,都是和委托和事件配合使用的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 Action a = ()=> { Console.WriteLine(" " ); }; a(); Action<int > a =(int b)=> { }; a2(100 ); Action<int >a3= (value )=> { } Func<string ,int >a4=(value )=> { return 1 ; } class Test { public event Action action; public Test () { int value =30 ; action=()=> { Console.WriteLine(value ); }; } }
List排序 List自带的排序方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 List<int >list=new List<int >(); list.Add(2 ); list.Add(3 ); list.Add(1 ); list.Sort(); class Item :IComparable <Item >{ public int money; public Item (int money ) { this .money=money; } public int Comparable (Item other ) { if (this .money>other.money) { return 1 ; } else { return -1 ; } } } List<Item>itemList=new List<Item>(); itemList.Add(new Item(34 )); itemList.Add(new Item(12 )); itemList.Add(new Item(23 )); itemList.Add(new Item(90 )); itemList.Add(new Item(89 )); itemList.Sort(); class ShopItem { public int id; public ShopItem (int id ) { this .id=id; } } List<ShopItem>shopItems=new List<ShopItem>(); shopItems.Add(new ShopItem(3 )); shopItems.Add(new ShopItem(2 )); shopItems.Add(new ShopItem(1 )); shopItems.Add(new ShopItem(6 )); shopItems.Add(new ShopItem(9 )); static int SortShopItem (ShopItem a,ShopItem b ){ if (a.id>b.id) { return 1 ; } else return -1 ; return 0 ; } ShopItems.Sort((a,b)=>{return a.id-b.id};); shopItems.Sort(SortShopItem);
协变逆变 概念:协变:和谐的变化。因为里氏替换原则,父类可以装载子类,比如string变成object。
逆变:不正常的变化。比如object变成string。
协变和逆变是用来修饰泛型的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 delegate T TestOut <out T >() ;delegate void TestIn <in T >(T t ) ; delegate T TestOut <out T >() ;delegate void TestIn <in T >(T t ) ;class Father { } class Son :Father { } Main() { TestOut<Son>os=()=> { reutrn new Son () ; }; TestOut<Father>of=os; Father f=of(); TestIn<Father>iF=(value )=> { }; TestIn<Son>iS=iF; iS(new Son()) }
多线程 进程(process)是计算机的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。打开一次应用程序就是在操作系统中开启了一个进程,进程之间可以相互独立运行,互不干扰,也可以相互访问,操作。
线程,操作系统中能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位,一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程(简单理解:就是代码从上到下运行的一条管道)
多线程
我们可以通过代码开启新的线程。
同时运行代码的多条管道,就叫多线程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 Thread t=new Thread(NewThreadLogic); t.Start(); t.IsBackGround=true ; t.ABort(); t=null ; static void NewThreadLogic (){ while (true ) { } } Thread.Sleep();
预处理器指令 编译器:编译器是一种翻译程序,它用于将源语言程序翻译为目标语言程序
源语言程序:某种程序设计语言写成的,比如c#、c++等语言写成的程序
目标语言程序:二进制数表示的伪机器代码写的程序
预处理器指令:知道编译器在实际编译开始之前对信息进行预处理
预处理器都是以#开始
预处理器指令不是语句,所以他们不以分号结束,目前我们经常用到的折叠代码块就是预处理器指令
反射 程序集:是经由编译器编译得到的,供进一步编译执行的那个中间产物,在windows系统中,他一般表现为后缀为:.dll(代码库文件),或者是.exe(可执行文件)的格式。
程序集就是我们写的一个代码集合,我们现在的所有代码最终都会被编译器翻译为一个程序集供别人使用
元数据: 用来描述数据的数据。程序中的类、变量等等信息就是程序的元数据,保存在数据集中
反射:程序在运行时可以查看其它程序集或者自身的元数据。一个运行的程序查看本身或者其他程序的元数据的行为就叫做反射。就是在程序运行时,通过反射可以得到其他程序及或者他们自己程序集代码的各种信息。类、函数、变量、对象等等。实例化他们,执行他们。操作他们
反射可以在程序编译后获得信息,所以它提高了程序的拓展性和灵活性。
程序运行时得到所有元数据,包括元数据的特性。
程序运行时,实例化对象,操作对象
程序运行时创建新对象,用这些对象执行任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 class Test { private int i=1 ; public int j=0 ; public string str="123" ; public Test () { } public Test (int i ) { this .i=i; } public Test (int i,string str ):this (i ) { this .str=str; } public void Speak () { } } int a=32 ;Type type=a.GetType(); Console.Write(Type); Type type2=typeof (int ); Console.Write(type2); Type type3=Type.GetType("System.Int32" ); Console.WriteLine(type3); Console.WriteLine(type1.Assembly);f Type t=typeof (Test); MemberInfo[]infos=t.GetMembers(); for (int i=0 ;i<infos.Length;i++){ Console.WriteLine(infos[i]); } ConstructorInfo[] ctors=t.GetConstructors(); for (int i=0 ;i<ctors.Length;i++){ Console.WriteLine(ctors[i]); } ConstructInfo info=info=t.GetConstructor(new Type[0 ]); Test obj=info.Invoke(null ) as Test; ConstructInfo info2=t.GetConstructor(new Type[] {typeof (int )}); obj=info2.Invoke(new object []{2 })as Test; Console.WriteLine(obj.str); FieldInfo[] fieldInfos=t.GetFields(); for (int i=0 ;i<fieldinfos.Length;i++){ Console.WriteLine(fieldinfos[i]); } FieldInfo infoJ=t.GetField("j" ); Console.WriteLine(infoJ); Test test=new Test(); test.j=99 ; test.str="2222; //通过反射 获取对象的某个变量的值 Console.WriteLine(infoJ.GetValue(test)); //通过反射 设置指定对象的某个变量的值 infoJ.SetValue(test,100); Console.WriteLine(infoJ.GetValue(Test)); //获取类的公共成员方法 //通过Type类中的GetMethod方法,得到类中的方法 //MethodInfo是方法的反射信息 Type strType=typeof(string); //如果存在方法重载 用Type数组表示参数类型 MethodInfo[] methods=strType.GetMethods(); for(int i=0;i<methods.Length;i++) { Console.WriteLine(methods[i]); } //Activator //用于快速实例化对象的类 //用于将Type对象快捷实例化为对象 //先得到Type //然后 快速实例化一个对象 Type testType =typeof(Test); //无参构造 Test testObj=Activator.CreateInstace(testType) as Test; Console.WriteLine(testObj.str); //有参数构造 testObj=Activator.CreateInstance(testType,99)as Test;//调用一个参数的构造函数 Console.WriteLine(testObj.j); testObj=Activator.CreateInstance(testType,88," 14566 ")as Test; Console.WriteLine(testObj.j,testObj.str); //Assembly //程序集类 //主要用来加载其他程序集,加载后 //才能用Type来使用其他程序集中的信息 //比如想要使用不是自己程序集中的内容,首先要加载程序集 //比如 dll文件(库文件) //简单地把库文件看成一种代码仓库,他提供给使用者一些可以直接拿来用的变量,函数或类 //三种加载程序集的函数 //一般用来加载在同一文件下的其他程序集 //Assembly assembly=Assembly.Load(" 程序集名称");(同一工程) //Assembly assembly2=Assembly.LoadFrom(" 包含程序集清单的文件的名称或路径"); //Assembly assembly3=Assembly.LoadFile(" 要加载文件的完全限定路径"); //先加载一个指定程序集 Assembly assmbly=Assembly.LoadFrom(" 略"); Type[] types=assembly.GetTypes(); for(int i=0;i<types.Length;i++) { Console.WriteLine(types[i]); } //之后再加载程序集中的一个类对象才能使用反射 Type icon=assembly.GetType(" 略"); MemberInfo[] members=icon.GetMembers(); for(int i=0;i<members.Length;i++) { Console.WriteLine(Members[i]); } //通过反射实例化icon对象 //略
特性 特性是一种允许我们想程序的程序集添加元数据的语言结构,是用于保存程序结构信息的某种特殊类型的类。特性提供功能强大的方法以声明信息与c#代码相关联,特性与程序实体相关后即可在运行时使用反射查询特性信息。特性的目的是告诉编译器把程序结构的某组元数据嵌入程序集中。可以放置在几乎所有的声明中。特性本质是一个类,可以利用特性类为元数据添加额外信息,之后可以用反射来获取这些额外信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 class MyCustomAttribute :Attribute { public string info; public MyCustomAttribute (string info ) { this .info=info; } public void TestFun () { Console.WriteLine("特性的方法" ); } } [MyCustom("田鼠浩二" ) ] class MyClass { [MyCustom("这是一个成员变量" ) ] public int value ; [MuCustom("计算加法的函数" ) ] public void TestFun ([MyCustom("函数参数" )]int a) { } } class Program { static void Main (string [] args ) { Console.WriteLine("特性" ); Myclass mc=new MyClass(); Type t=mc.GetType(); t=typeof (MyClass); t=Type.GetType("" ); if (t.IsDefind(typeof (MyCustomAttribute),false )) { Console.WriteLine("该类型应用了MyCustom特性" ); } object []array= t.getCustomAttributes(true ); for (int i=0 ;i<array.Length;i++) { if (array[i] is MyCustomAttribute) { console.WriteLine((array[i] as MyCustomAttribute).info); (array[i] as MyCustomAttribute).TestFun(); } } } }