C#进阶
简单数据结构类
ArrayList
元素类型以Object类型存储,支持增删查改的数组容器。
因而存在装箱拆箱操作,谨慎使用。
//ArrayList
ArrayList array=new ArrayList();
//增=================
array.Add("Hello");
array.Add(true);
array.Add("Tony");//添加单个元素
array.Add("Hello");
array.Add("World");
ArrayList array2=new ArrayList();
array2.Add("123");
array.AddRange(array2);//从数组添加元素
//插入
array.Insert(1,"456");
//删除==================
array.Remove("Hello");
array.RemoveAt(1);//根据索引移除元素
array.Clear();//清空
//查=================
array[0];
//查看元素是否存储在
if(array.Contains(true))
{
}
//正向查找某个元素
//返回索引 未找到则返回-1
array.IndexOf("Hello");
//反向查找元素位置
array.LastIndexOf("Hello");
//改=======================
array[2]="123";
//遍历======
//区别长度(已经使用的容量)和容量
//容量
array.Capacity;
for(int i=0;i
ArrayList和数组区别?
- ArrayList使用不用说明固定长度,数组则需要
- 数组存储的是指定类型的,ArrayList是Object
- ArrayList存在装箱拆箱,数组不存在
- ArrayList数组长度用Count获取 而数组长度为Length
- ArrayList中提供增删方法,使用方便,数组需要自己操作实现
Stack
stack的本质:也是Object数组
//栈 先进后出
//声明
Stack stack=new Stack();
//========增===压栈
stack.Push(1);
stack.Push("23");
stack.Push("Tony");
stack.Push(66.6);
//=======取====弹栈
object o=stack.Pop();
//======查
//栈只能查看栈顶的内容,无索引器不可根据索引查看
Object look=stack.Peek();
//查看元素中有无内容
if(stack.Contains("Tony"))
{
Console.WriteLine("该元素存在!");
}
//清空
stack.Clear();
//遍历
foreach(object item in stack)
{
Console.WriteLine(item);
//从栈顶取元素
}
//将栈转换为数组
object[] stackArray=stack.ToArray();
//栈顶元素在数组前部分
//循环弹栈
while(stack.Count>0)
{
object obj=stack.Pop();
Console.WriteLine(obj);
}
//存在装箱拆箱
Queue
本质:也是一个object数组
//队列 先进先出
//声明
Queue queue=new Queue();
//增
queue.Enqueue(1);
queue.Enqueue(3.15f);
queue.Enqueue("Tony");
//取
queue.Dequeue();
//查
queue.Peek();
if(queue.Contains(3.15f))
{
//打印
}
//改 只能清空
queue.Clear();
//遍历
queue.Count();
foreach(object item in queue)
{
Console.WriteLine(item);
}
//转成数组
object[] objs=queue.ToArray();
for(int i=0;i0)
{
object obj=queue.Dequeue();
Console.WriteLine(obj);
}
Hashtable
本质:存储也是以object存储。散列表 ,基于hash代码组织起来的键值对
可以做到访问效率是O(1)
//HashTable
//声明
Hashtable hashtable=new Hashtable();
//增加 键不能重复
hashtable.Add(1,"123");
hashtable.Add("name","TonyChang");
hashtable.Add(3,21);
//删除 --只能通过键来删除
hashtable.Remove(1);
hashtable.Remove("name");
//清空
hashtable.Clear();
//查看 找不到为空
hashtable[1];
hashtable["name"];
//验证是否存在
if(hashtable.Contains("name"))
{
//根据键去查找
}
if(hashtable.ContainsKey("name"))
{
//根据键去查找
}
if(hashtable.ContainsValue("TonyChang"))
{
//根据值去查找
}
//只能通过键来找值,反之不行
//遍历
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 item in hashtable)
{
Console.WriteLine("键"+item.Key+" 值"+item.Value);
}
//迭代器遍历
IDictionaryEnumerator tcIDE=hashtable.GetEnumerator();
bool flag =tcIDE.MoveNext();
while(flag)
{
Console.WriteLine("键"+tcIDE.Key+" 值"+tcIDE.Value);
flag =tcIDE.MoveNext();
}
//存在装箱拆箱
泛型
泛型
泛型实现了类型参数化,达到代码重用目的,通过类型参数化来实现同一份代码操作多种类想,
泛型相当于类型占位符,定义类或者方法时使用替代符代表变量类型,
当真正使用类或方法时候再具体指定类型
泛型分类:泛型类,泛型方法,泛型接口
//泛型
class TestClass
{
public T value;
}
TestClass t=new TestClass();
t.value=666;
//泛型占位符可以有多个
class TestClass2
{
public T1 value1;
public T2 value2;
public K value3;
public M value4;
}
interface ITest
{
T Value
{
get;
set;
}
}
//继承之后需要表明具体类类型
class Demo:ITest
{
public int Value
{
get;
set;
}
}
//泛型方法
class Test2
{
public void TestFun(T value)
{
Console.WriteLine(value);
}
public void TestFun()
{
T t=default(T);
}
//作为返回值
public T fun3()
{
return default(T);
}
}
Test2 tt=new Test2();
tt.TestFun("Tony");
//泛型类中的泛型方法
class Test2
{
public T value;
public void TestFun(T value)
{
//这是非泛型方法
}
//泛型方法
//判断""有无
public void fun4(K value)
{
//打印
}
}
泛型约束
泛型约束:关键字 where
- 值类型 where T :struct
- 引用类型 where T :class
- 存在无参公共构造函数 where T :new ()
- 某个类本身或者其派生类 where T:类名
- 某个接口的派生类型 where T:接口名
- 另一个泛型类型本身或者派生类型 where T:另一个泛型字符
注:这里的”T“ 可以换成其它的泛型字母
//泛型类型的约束
class Test where T:struct
{
public void fun1 where M:struct
{
}
}
class Test1 where T:class
{
public void fun1 where M:struct
{
}
}
//注意抽象类的无参公共构造函数
//也被允许
class Test2 where T:new()
{
public void fun1 where M:struct
{
}
}
//
//约束的组合使用
class Test7 where T:class,new ()
{
}
//多个泛型有有约束
class Test8 where T:class,new() where K:struct
{
}
常用的泛型数据结构类
list
本质:泛型数组
//List
List list=new List();
//添加
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
//查
list[0];
//清空
list.Clear();
//移除
list.RemoveAt(1);
//查看某个元素是否存在
if(list.Contains(1))
{
//该元素存在
}
//查找元素索引
//未找到返回-1
int index=list.IndexOf(4);
//反向查找
//未找到返回-1
index=list.LastIndexOf(4);
//改
list[2]=66;
//插入
list.Insert(0,666);
//遍历
//长度
list.Count;
//容量
list.Capacity;
for(int i=0;i
DIctionary
本质:有具体类型的hashtable
//Dictionary
//声明
Dictionary dictionary=new Dictionary();
//增
dictionary.Add(1,"Hello");
dictionary.Add(2,"World");
dictionary.Add(3,"Tony");
dictionary.Add(4,"Chang");
//删除
dictionary.Remove(1);
//清空
dictionary.Clear();
//查
//按键进行查
dictionary[3];
dictionary[5];//找不到则报错!!!
//查看是否存在
if(dictionary.ContainsKey(1))
{
//存在
}
if(dictionary.ContainsValue("Tony"))
{
//存在
}
//改
dictionary[1]="258";
//遍历
dictionary.Count;//元素个数
foreach(int item in dictionary.Keys)
{
Console.WriteLine(item);
Console.WriteLine(dictionary[item]);
}
//遍历所有类型的值
foreach(int item in dictionary.Values)
{
Console.WriteLine(item);
}
//键值对一起查找
foreach(KeyValuePair item in dictionary)
{
Console.WriteLine("键:"+item.Key+"值:"+item.Value);
}
顺序存储和链式存储
数据结构
线性表:数组、链表、栈、队列
非线性表:树、图、堆、散列表
存储结构:
- 顺序存储
- 数组,List,ArrayList
- Stack
- Queue
- 链式存储
- 单向链表
- 双向链表
- 循环链表
//简单的单向链表
//节点类
class LinkedNode
{
public T value;
public LinkedNode nextNode;
public LinkedNode(T value)
{
this.value=value;
}
}
//单向链表的简单实现
class LinkdedList
{
public LinkedNode head;
public LinkedNode last;
public void Add(T value)
{
LinkedNode node=new LinkedNode(value);
if(head==null)
{
head=node;
last=node;
}
else
{
last.nextNode=node;
last=node;
}
}
public void Remove(T value)
{
if(head==null)
{
return;
}
if(head.value.Equals(value))
{
//如果只有一个节点
//且还是要删除的节点
head=head.nextNode;
if(head==null)
{
last==null;
}
return;
}
LinkedNode node=head;//哨兵节点
//走到目标节点前的一个节点
while(node.nextNode!=null)
{
if(node.nextNode.value.Equals(value))
{
break;
}
node=node.nextNode;
}
//进行移除
node.nextNode=node.nextNode.nextNode;
}
}
链式和数组的优缺点:
链式表的增、删比较方便,只需要更改节点之间的联系即可。
数组表查找比较方便,可以根据索引直接定位到某个位置。
LinkedList
有类型的双向链表。
//双向链表
LinkedList linkedList=new LinkedList();
//增
//从尾部添加
linkedList.AddLast(10);
//从头部加
linkedList.AddFirst(5);
//移除
linkedList.RemoveFirst();//移除头节点
linkedList.RemoveLast();//移除尾节点
//移除元素
linkedList.Remove(5);
//清空
linkedList.Clear();
//查
//头节点
LinkedListNode first=linkedList.First;
//尾节点
LinkedListNode last=linkedList.Last;
//找到某个节点
LinkedListNode node=linkedList.Find(5);//找不到返回为null
//在某个节点之后添加一个节点
linkedList.AddAfter(node,15);
//在某个节点之后前添加一个节点
linkedList.AddBefore(node,12);
//判断某一元素是否存在
if(linkedList.Contains(5))
{
//链表中存在5
}
//改
//找到某一节点
//改变其数值
//找到某个节点
LinkedListNode node1=linkedList.Find(5);//找不到返回为null
node1.Value=15;
//遍历
foreach(int item in linkedList)
{
//打印节点
Console.WriteLine(item);
}
//从头节点进行遍历查找
LinkedListNode curNode=linkedList.First;
while(curNode!=null)
{
Console.WriteLine(curNode.Value);
curNode=curNode.Next;
}
//从尾部节点进行遍历查找
LinkedListNode curNode=linkedList.Last;
while(curNode!=null)
{
Console.WriteLine(curNode.Value);
curNode=curNode.PreVious;
}
泛型栈和队列
Stack stack;
Queue queue;
具体的api和非泛型的相同。不再赘述!
委托与事件
委托
委托是函数的容器,可以理解为表示函数的变量类型,用来存储、传递函数。
委托的本质是一个类,用来定义函数的类型(返回值和参数的类型)
不同的函数必须对应各自“格式”的委托。
关键字:delegate
存在位置:namespace 中(一般在此处),class中
//委托
//帕斯卡命名法
delegate void MyFuns();//无参无返回值的委托(此类型函数的家)
delegate int MyFuns2(int a);//不可以重名!!!尽管类型不同也不可以
//默认为public的,一般不用private
class Program
{
static void Main(string[] args)
{
//将Fun函数放入委托容器中
MyFuns funs=new MyFuns(Fun);
//调用委托(中的函数)
funs.Invoke();
//======或者
MyFuns f2=Fun;//声明委托
f2();//调用委托
//============================
MyFuns2 funs2=new MyFuns2(Fun2);
funs2.Invoke(2);
MyFuns2 f2=Fun2;//声明
f2(2);//调用
}
static void Fun()
{
Console.WriteLine("我是个函数!");
}
static void Fun4()
{
Console.WriteLine("我是个函数Fun4!");
}
static int Fun2(int a)
{
Console.WriteLine("我是另外一种类型函数");
return a*2;
}
}
委托变量是函数的容器:
委托常用在:
- 作为类的成员
- 作为函数的参数
//接上一个代码块
class Test
{
public MyFuns funs;
public MyFuns2 funs2;
//委托作为函数的参数
public void TestFun(MyFuns funs,MyFuns2 funs2)
{
//先处理一些逻辑
//逻辑 code
//
//再执行委托方法
this.funs=funs;
this.funs2=funs2;
}
}
//在上个代码块Main中调用
//使用
Test t=new Test();
t.TestFun(Fun,Fun2);//传递的函数名字
委托中存储多个函数(多播委托)
//多播委托
MyFuns funs3=null;
fun3+=Fun;
fun3+=Fun4;
fun3.Invoke();//执行
//本质:观察者模式====
//老板来了!可以执行委托,通知存储在委托中的员工好好工作的函数执行。
//老板走了!可以执行另一种委托,通知存储在委托中的员工开始摸鱼。(狗头)
//委托轻松实现!
小点:委托中多次减去同一个函数,不会报错。
委托执行之前最好要检查是否为空!
系统提供的委托:
//系统定义好的委托
//Action是无参无返回值类型的委托
Action action=Fun;
action+=Fun4;
action();//执行
//可以传参数无返回值的委托
Action ac1;
Action ac2;
//=========Action无返回值委托===========
//=========Func有返回值=================
//Func委托
Func funcInt;//返回值为int 无参数的委托
Func func2;//参数为int 返回值为float的委托
funcInt+=Funs;
static int Fun5()
{
Console.WriteLine("我是函数Fun5");
return 5;
}
//自定义泛型委托
delegate T MyFun3(T value);
相当于给每个装在进委托中的函数来一次单独的分配一个新的委托,各自的函数都存入自己独立的委托,然后调用单独的委托便会执行在委托中的函数。(对foreach遍历的理解)
委托练习:
大耳朵图图一家吃饭!
使用托通知大家吃饭。
namespace TC
{
abstract class Person
{
public string name;
public abstract void Eat();
}
class Father : Person
{
public Father(string name)
{
this.name = name;
}
public override void Eat()
{
Console.WriteLine("{0}来吃饭了", name);
}
}
class Mother : Person
{
public Action ToEat;
public Mother(string name)
{
this.name = name;
}
public override void Eat()
{
Console.WriteLine("{0}来吃饭了", name);
}
public void Cooking()
{
Console.WriteLine("{0}正在做饭,",name);
Console.WriteLine("做完了");
ToEat?.Invoke();
Eat();
}
}
class Son : Person
{
public Son(string name)
{
this.name = name;
}
public override void Eat()
{
Console.WriteLine("{0}来吃饭了", name);
}
}
class Program
{
static void Main(string[] args)
{
Father father = new Father("胡英俊");
Mother mother = new Mother("张小丽");
Son son = new Son("胡图图");
mother.ToEat += father.Eat;
mother.ToEat += son.Eat;
mother.Cooking();
}
}
}
事件
事件是基于委托的存在,是安全的委托。
事件的使用:
- 事件是作为成员变量存在于子类中
- 其它用法和委托相同
事件和委托的不同之处:事件不能在类外部赋值(但可以进行+=,-=)和调用。(事件必须在类中赋值和使用)
注意:它只能作为成员存在于类和接口以及结构体中。
class Test
{
//委托
public Action myAction;
//事件
public event Action myEvent;
}
为什么要用事件呢?
- 防止外部随意的置空委托
- 防止外部随意调用委托
- 事件对委托进行一次的封装,使用更安全
匿名函数
匿名函数没有函数名字,往往都配合事件和委托使用的。
基本语法:
delegate(参数列表)``{
//函数逻辑
};
何时使用:
- 函数中传递委托参数时
- 委托或事件赋值时候
//匿名函数
//无参 无返回
Action action = delegate()
{
//匿名函数的声明
//匿名函数声明,放置在委托容器中
Console.WriteLine("我是匿名函数");
};
action();//调用委托时候 调用匿名函数
//有参 无返回值
Action action2=delegate(int a,string b)
{
Console.WriteLine(a);
Console.WriteLine(b);
};//注意:匿名函数最后加分号
action2(100,"Tony");
//有返回值 无参数
Func action3=delegate()
{
return 666;
};
action3();
//一般情况下会作为函数参数传递
class Test
{
public Action action;
//作为参数
public void DoSomething(int a,Action ac)
{
Console.WriteLine(a);
ac();
}
//作为返回值
public void GetFun()
{
return delegate(){
//无参数无返回值的匿名函数
//作为函数的返回值
};
}
}
//使用
Test t=new Test();
t.DoSomething(100,delegate(){
Console.WriteLine("随函数传入的匿名函数逻辑");
});
//接受返回的匿名函数
Action ac3=t.GetFun();
//执行
ac3();
//或者一步到位
t.GetFun()();
//匿名函数的缺点:
//匿名函数添加到委托中,因为没有名字,不方便进行管理。
//不能指定移除某个匿名函数
Lambda表达式
lambda只是匿名函数的简写形式,本质还是匿名函数
//Lambda表达式
//无参无返回值
Action action=()=>{
Console.WriteLine("无参无返回值的");
};
//执行
action();
//====有参数 无返回值
Action action2=(int value)=>
{
Console.WriteLine("有参无返回值的Lambda{0}",value);
};
//调用执行
action2(666);
//可以省略参数类型,参数类型和存储其的容器(事件或委托)来判断
Action action3=(value)=>{
Console.WriteLine("省略了参数类型的Lambda表达式的写法{0}",value);
};
action(999);
//======有返回值=======
Func action4=(value)=>{
Console.WriteLine("有参有返回值的Lambda表达式{0}",value);
return 666;
};
int a=action4("hhh");
闭包:
内层函数可以引用包含在它外层函数的变量,即便外层函数的执行已经终止。
注意:该变量的值并非创建变量时候的初始值,而是在外层函数处理过的最终值。
//例如
static FuncTestFun(int i)
{
return delegate(int v)
{
//内部函数占用着外部函数的i
return i*v;
}
}
//该变量的值并非创建变量时候的初始值,而是在外层函数处理过的最终值。
class Test
{
public event Action action;
public Test()
{
int value=66;
action=()=>
{
Console.WriteLine("占用外部的value");
};
//再次给事件中添加匿名函数
for(int i=0;i{
//此时所有的i
//为外部函数执行完循环之后的最终值
//最终值i=10;
Console.WriteLine(i);
};
}
//此时action中共有个匿名函数
//第一个函数中value=66
//其余for循环添加的匿名函数参数i的值均为10
//对比:
//再次给事件中添加匿名函数
for(int i=0;i{
//此时所有的i
//为外部函数执行完循环之后的最终值
//最终值i=10;
Console.WriteLine(index);
};
}
//第一个函数中value=66
//中间的for循环添加的匿名函数参数i的值均为10
//最后的for循环添加的匿名函数参数index的值则是0~9
//因为添加的是index临时变量,index的数值就是自身最终数值 而i还在for
//循环中自增
}
}
List排序
//List排序
List list=new List();
list.Add(8);
list.Add(3);
list.Add(2);
list.Add(5);
list.Add(1);
list.Add(6);
list.Add(4);
list.Add(9);
list.Add(7);
//1 List自带的排序
list.Sort();
//ArrayList中也有Sort排序! 但是object类如何排序? 拆箱比较?
//自定义类排序
//若想使用排序函数 要实现一个排序接口
class Item:IComparable
{
public int money;
public Item(int money)
{
this.money=money;
}
//排序规则函数
//List.Sort()根据存储元素的方法来做排序
public int CompareTo(Item other)
{
//返回值>0往后排
//返回值=0保持不变
//返回值other.money)
{
return 1;//this对象移动到other对象后面
}else
{
return -1;//this对象移到other对象前面
}
}
}
List itemList=new List();
itemList.Add(new Item(55));
itemList.Add(new Item(98));
itemList.Add(new Item(35));
itemList.Add(new Item(72));
itemList.Add(new Item(89));
itemList.Sort();//自定义排序规则调用排序
//小理解:List类在调用Sort排序时候,会将元素类型 as为排序接口类型(里氏替换原则)
//然后调用其CompareTo方法进行比较。
//3 通过委托函数进行排序
class ShopItem
{
public int id;
public ShopItem(int id)
{
this.id=id;
}
}
List shopItems=new List();
shopItems.Add(new ShopItem(2));
shopItems.Add(new ShopItem(1));
shopItems.Add(new ShopItem(6));
shopItems.Add(new ShopItem(5));
shopItems.Add(new ShopItem(3));
shopItems.Add(new ShopItem(4));
//传入中的对象为
//列表中的两个元素 在比较时候会传入元素做比较
static int SortShopItem(ShopItem a,ShopItem b)
{
if(a.id>b.id)
{
return 1;
}else
{
return -1;
}
}
//调用Sort排序
//使用Sort的带有委托参数的重载函数
shopItems.Sort(SortShopItem);
//使用匿名内部类
shopItems.Sort(delegate (ShopItem a,ShopItem b)
{
if(a.id>b.id)
{
return 1;
}else
{
return -1;
}
});
//也可以使用Lambda表达式
shopItems.Sort((a,b)=>
{
return a.id>b.id?1:-1;
});
//遍历结果
for(int i=0;i
协变和逆变
协变:和谐的变化。例如,里氏替换原则,父类可以装子类,因此子类变父类是一种和谐的变化。string变成object(装箱)也是协变。(可以理解小变大,顺应形势,理所应当。)
逆变:于协变相反,逆常规的变化,不正常的变化,子类装父类,object变string就属于协变。(可以理解大变小,强制转换,扭扭捏捏)
协变和逆变是用来修饰泛型的,只有在泛型接口和泛型委托中修饰泛型字符。
关键字:
协变:out(顺势而为,顺出)修饰的只能作为 返回值
逆变:in(逆流而上,逆进)修饰的只能作为 参数
//协变和逆变
//out修饰类型 作为返回值
delegate T TestFunOut();
//in修饰的泛型 只能作为参数
delegate void TestFunIn(in T)(T 服务器托管网t);
结合里氏替换原则:
class Father
{
}
class Son:Father
{
}
class Program
{
static void Main(string[] args)
{
TestFunOut os=()=>{
return new Son();
};
TestFunOut of=os;//父类型的委托装载子类型的委托,二者都是委托的返回值
//符合out 协变 如果没有用out修饰,则说明二者是不同返回值类型的
//委托,不可以进行赋值。
TestFunIn InF=(value)=>
{
};
TestFunIn InS=InF;
Ins(new Son());
//子类型的委托接受父类型的委托,但二者都是属于委托参数,可以理解为,
//子类型的构造函数调用父类型的构造函数来构造,底层本质还是符合大装小的原则
//in修饰之后,才可以进行大小委托赋值。是一种逆变
}
}
多线程
进程?
进程是运行中的程序,系统进行资源分配和调度的基本单位,是操作系统的基础。
线程?
线程存在于进程中,是进程中的实际运作单位,是操作系统能够进行运算调度的最小单位。一个进程中可以并发多个线程,他们共享利用进程中的堆栈资源。
多线程?
一个进程中除了必要的主线程之外,可以开启其它的线程来完成一些计算处理。
一个进程中除了必要的主线程之外,可以开启其它的线程来完成一些计算处理。
//线程
//声明
Thread t=new Thread(MyThreadFun);
bool isRunning=true;
static void MyThreadFun()
{
while(isRunning)
{
Thread.Sleep(10000);
Console.WriteLine("新线程的执行函数");
}
}
//启动
t.Start();
//后台线程
//设置为后台线程之线程受主线程的影响(主线程结束,其它线程也结束),后台线程不会防止应用程序进程被终止
//如果不设置为后台线程可能会导致进程无法正常关闭
t.IsBackground= true;
//关闭线程
//如果线程中有死循环一致执行的,要关闭线程
//主动结束死循环
//对while()判断条件控制设为false
isRunning=false;
//或者通过对线程提供的方法
//不一定都通用
t.Abort();
t=null;
//线程休眠
Thread.Sleep(10000);
//多线程--线程锁
//避免对一个资源同时操作会造成逻辑错误
//多线程中要使用lock 保证一个内存区一刻时间只能有一个线程访问
Object obj=new Object();
//锁的使用引用类型
lock(obj)
{
//逻辑
}
预处理器指令
预处理是在源程序编译成目标指令之前,可以简单的帮助选择一些条件是否要编译执行。
常见的预处理指令:
#define Tony
#define Lili
#if Tony
Console.WriteLine("Tony");
#elseif
#endif Lili
Console.WriteLine("Lili");
#else
Console.WriteLine("其他人");
#undef Tony
#waring
#error
反射和特性
反射:
程序正在运行时候,可以查看其它程序集或者自身的元数据。一个运行的程序查看本身或者其它程序的元数据的行为就叫反射。
我们知道,程序=数据+算法,程序在执行过程中需要不断地给它“投喂”数据养料,而反射就是获取养料(类、函数,变量等)的过程。有了反射,在运行时候可以动态的获取自身所需要的资源,可以提高程序的拓展性和灵活性服务器托管网。
程序集:
程序集是经由编译器编译得到的,供进一步编译执行的那个中间产物。在WINDOWS系统中,它一般表现为后缀为dll(库文件)或者是exe(可执行文件)的格式。
也就是说程序集是我们开发的项目中程序的集合,工程文件编译运行所需要执行的所有文件资源都算作程序集中的内容。
元数据:
元数据就是用来描述数据的数据,例如在程序中,类、函数、变量等等信息就是程序的元数据,她们保存在程序集中。
Type:
它是反射功能的基础,访问元数据的主要方式.
Assembly
//Type
//获取Type
//1 objet中的方法
int a=45;
Type type=a.GetType();
//2通过typeof获取
Type type2=typeof(int);
//3通过类名字
//要说明类所在的命名空间
Type type3=Type.GetType("System.Int32");
//====三个访问方式得到的Type类型相同
//得到类的程序集的信息 Assembly
Console.WriteLine(type.Assembly);
Console.WriteLine(type2.Assembly);
Console.WriteLine(type3.Assembly);
//===========================
class Test
{
private int i=1;
private int j=2;
private string name;
public Test(int i)
{
this.i=i;
}
public Test(int i,string str):this(i)
{
this.name=str;
}
public void Talk()
{
Console.WriteLine("123");
}
}
//获取类中所有的公共成员
Type type=typeof(Test);
MemeberInfo[] infos=type.GetMembers();
for (int i = 0; i
特性
本质是个类,可以对元数据的解释和说明,供反射获取的时候来获取被调用的元数据的信息。
系统生成的特性:
过时特性:
迭代器
迭代器iterator
有时也称为光标,是一种的软件设计模式,迭代器模式提供一个方法顺序访问一个聚合对象中的各个元素,并且不暴露其内部的标识。
实现了迭代器的类才可以使用foreach遍历。
//迭代器
//继承两个接口实现
//光标移动
//迭代器
class MyList:IEnumerable,IEnumerator
{
private int[] list;
private int position = -1;
public MyList()
{
list = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
}
//实现IEnumerable的接口方法
public IEnumerator GetEnumerator()
{
Reset();//返回之前要将索引器回到0
return this;
}
//=========================
public object Current
{
get => list[position];
}
//移动光标
public bool MoveNext()
{
++position;
return position
试着理解foreach的本质:
- 先获取要遍历对象的IEnumerator,调用其中的GetEnumerator方法获取
- 执行IEnumerator对象中的MoveNext方法
- 若返回为true,则继续调用Current获取得到值给item
使用语法糖实现迭代器
//迭代器
class MyList1:IEnumerable
{
private int[] list;
public MyList1()
{
list = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
}
//实现IEnumerable的接口方法
public IEnumerator GetEnumerator()
{
for(int i=0;i
使用语法糖为泛型数组实现迭代器
//泛型实现迭代器类
class MyList : IEnumerable
{
private T[] array;
public MyList(params T[] array)
{
this.array = array;
}
public IEnumerator GetEnumerator()
{
for (int i = 0; i
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
前言华为云数据库 GaussDB是一款拥有云上高可用,高可靠,高安全,弹性伸缩,一键部署,快速备份恢复,监控告警等关键能力,能为企业提供功能全面,稳定可靠,扩展性强,性能优越的企业级数据库服务。同时具有PB级海量数据存储、实时高效访问、自动化运维等特点,广泛应…