C#進階
簡單數(shù)據(jù)結(jié)構(gòu)類
ArrayList
元素類型以O(shè)bject類型存儲,支持增刪查改的數(shù)組容器。
因而存在裝箱拆箱操作,謹慎使用。
//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);//從數(shù)組添加元素
//插入
array.Insert(1,"456");
//刪除==================
array.Remove("Hello");
array.RemoveAt(1);//根據(jù)索引移除元素
array.Clear();//清空
//查=================
array[0];
//查看元素是否存儲在
if(array.Contains(true))
{
}
//正向查找某個元素
//返回索引 未找到則返回-1
array.IndexOf("Hello");
//反向查找元素位置
array.LastIndexOf("Hello");
//改=======================
array[2]="123";
//遍歷======
//區(qū)別長度(已經(jīng)使用的容量)和容量
//容量
array.Capacity;
for(int i=0;i<array.Count;i++)
{
Console.WriteLine(array[i]);
}
//迭代器遍歷
foreach(object obj in array)
{
Console.WriteLine(item);
}
//裝箱拆箱
ArrayList和數(shù)組區(qū)別?
- ArrayList使用不用說明固定長度,數(shù)組則需要
- 數(shù)組存儲的是指定類型的,ArrayList是Object
- ArrayList存在裝箱拆箱,數(shù)組不存在
- ArrayList數(shù)組長度用Count獲取 而數(shù)組長度為Length
- ArrayList中提供增刪方法,使用方便,數(shù)組需要自己操作實現(xiàn)
Stack
stack的本質(zhì):也是Object數(shù)組
//棧 先進后出
//聲明
Stack stack=new Stack();
//========增===壓棧
stack.Push(1);
stack.Push("23");
stack.Push("Tony");
stack.Push(66.6);
//=======取====彈棧
object o=stack.Pop();
//======查
//棧只能查看棧頂?shù)膬?nèi)容,無索引器不可根據(jù)索引查看
Object look=stack.Peek();
//查看元素中有無內(nèi)容
if(stack.Contains("Tony"))
{
Console.WriteLine("該元素存在!");
}
//清空
stack.Clear();
//遍歷
foreach(object item in stack)
{
Console.WriteLine(item);
//從棧頂取元素
}
//將棧轉(zhuǎn)換為數(shù)組
object[] stackArray=stack.ToArray();
//棧頂元素在數(shù)組前部分
//循環(huán)彈棧
while(stack.Count>0)
{
object obj=stack.Pop();
Console.WriteLine(obj);
}
//存在裝箱拆箱
Queue
本質(zhì):也是一個object數(shù)組
//隊列 先進先出
//聲明
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);
}
//轉(zhuǎn)成數(shù)組
object[] objs=queue.ToArray();
for(int i=0;i<objs.Length;i++)
{
//打印
Console.WriteLine(objs[i]);
}
//循環(huán)出列
while(queue.Count>0)
{
object obj=queue.Dequeue();
Console.WriteLine(obj);
}
Hashtable
本質(zhì):存儲也是以object存儲。散列表 ,基于hash代碼組織起來的鍵值對
可以做到訪問效率是O(1)
//HashTable
//聲明
Hashtable hashtable=new Hashtable();
//增加 鍵不能重復(fù)
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"))
{
//根據(jù)鍵去查找
}
if(hashtable.ContainsKey("name"))
{
//根據(jù)鍵去查找
}
if(hashtable.ContainsValue("TonyChang"))
{
//根據(jù)值去查找
}
//只能通過鍵來找值,反之不行
//遍歷
hashtable.Count;//鍵值對數(shù)
//不一定按照插入順序打印
//元素是無序的
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();
}
//存在裝箱拆箱
泛型
泛型
泛型實現(xiàn)了類型參數(shù)化,達到代碼重用目的,通過類型參數(shù)化來實現(xiàn)同一份代碼操作多種類想,
泛型相當(dāng)于類型占位符,定義類或者方法時使用替代符代表變量類型,
當(dāng)真正使用類或方法時候再具體指定類型
泛型分類:泛型類,泛型方法,泛型接口
//泛型
class TestClass<T>
{
public T value;
}
TestClass<int> t=new TestClass<int>();
t.value=666;
//泛型占位符可以有多個
class TestClass2<T1,T2,K,M>
{
public T1 value1;
public T2 value2;
public K value3;
public M value4;
}
interface ITest<T>
{
T Value
{
get;
set;
}
}
//繼承之后需要表明具體類類型
class Demo:ITest<int>
{
public int Value
{
get;
set;
}
}
//泛型方法
class Test2
{
public void TestFun<T>(T value)
{
Console.WriteLine(value);
}
public void TestFun<T>()
{
T t=default(T);
}
//作為返回值
public T fun3<T>()
{
return default(T);
}
}
Test2 tt=new Test2();
tt.TestFun<string>("Tony");
//泛型類中的泛型方法
class Test2<T>
{
public T value;
public void TestFun(T value)
{
//這是非泛型方法
}
//泛型方法
//判斷"<>"有無
public void fun4<K>(K value)
{
//打印
}
}
泛型約束
泛型約束:關(guān)鍵字 where
- 值類型 where T :struct
- 引用類型 where T :class
- 存在無參公共構(gòu)造函數(shù) where T :new ()
- 某個類本身或者其派生類 where T:類名
- 某個接口的派生類型 where T:接口名
- 另一個泛型類型本身或者派生類型 where T:另一個泛型字符
注:這里的”T“ 可以換成其它的泛型字母
//泛型類型的約束
class Test<T> where T:struct
{
public void fun1<M> where M:struct
{
}
}
class Test1<T> where T:class
{
public void fun1<M> where M:struct
{
}
}
//注意抽象類的無參公共構(gòu)造函數(shù)
//也被允許
class Test2<T> where T:new()
{
public void fun1<M> where M:struct
{
}
}
//······
//約束的組合使用
class Test7<T> where T:class,new ()
{
}
//多個泛型有有約束
class Test8<T,K> where T:class,new() where K:struct
{
}
常用的泛型數(shù)據(jù)結(jié)構(gòu)類
list
本質(zhì):泛型數(shù)組
//List
List<int> list=new List<int>();
//添加
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<list.Count;i++)
{
//打印
Console.WriteLine(list[i]);
}
foreach(int item in list)
{
Console.WriteLine(item);
}
//List指定類型 不存在裝箱拆箱
DIctionary
本質(zhì):有具體類型的hashtable
//Dictionary
//聲明
Dictionary<int,string> dictionary=new Dictionary<int,string>();
//增
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;//元素個數(shù)
foreach(int item in dictionary.Keys)
{
Console.WriteLine(item);
Console.WriteLine(dictionary[item]);
}
//遍歷所有類型的值
foreach(int item in dictionary.Values)
{
Console.WriteLine(item);
}
//鍵值對一起查找
foreach(KeyValuePair<int,string> item in dictionary)
{
Console.WriteLine("鍵:"+item.Key+"值:"+item.Value);
}
順序存儲和鏈?zhǔn)酱鎯?/h4>
數(shù)據(jù)結(jié)構(gòu)
線性表:數(shù)組、鏈表、棧、隊列
非線性表:樹、圖、堆、散列表
存儲結(jié)構(gòu):
- 順序存儲
- 數(shù)組,List,ArrayList
- Stack
- Queue
- 鏈?zhǔn)酱鎯?
- 單向鏈表
- 雙向鏈表
- 循環(huán)鏈表
//簡單的單向鏈表
//節(jié)點類
class LinkedNode<T>
{
public T value;
public LinkedNode<T> nextNode;
public LinkedNode(T value)
{
this.value=value;
}
}
//單向鏈表的簡單實現(xiàn)
class LinkdedList<T>
{
public LinkedNode<T> head;
public LinkedNode<T> last;
public void Add(T value)
{
LinkedNode<T> node=new LinkedNode<T>(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))
{
//如果只有一個節(jié)點
//且還是要刪除的節(jié)點
head=head.nextNode;
if(head==null)
{
last==null;
}
return;
}
LinkedNode<T> node=head;//哨兵節(jié)點
//走到目標(biāo)節(jié)點前的一個節(jié)點
while(node.nextNode!=null)
{
if(node.nextNode.value.Equals(value))
{
break;
}
node=node.nextNode;
}
//進行移除
node.nextNode=node.nextNode.nextNode;
}
}
鏈?zhǔn)胶蛿?shù)組的優(yōu)缺點:
鏈?zhǔn)奖淼脑觥h比較方便,只需要更改節(jié)點之間的聯(lián)系即可。
數(shù)組表查找比較方便,可以根據(jù)索引直接定位到某個位置。
LinkedList
有類型的雙向鏈表。
//雙向鏈表
LinkedList<int> linkedList=new LinkedList<int>();
//增
//從尾部添加
linkedList.AddLast(10);
//從頭部加
linkedList.AddFirst(5);
//移除
linkedList.RemoveFirst();//移除頭節(jié)點
linkedList.RemoveLast();//移除尾節(jié)點
//移除元素
linkedList.Remove(5);
//清空
linkedList.Clear();
//查
//頭節(jié)點
LinkedListNode<int> first=linkedList.First;
//尾節(jié)點
LinkedListNode<int> last=linkedList.Last;
//找到某個節(jié)點
LinkedListNode<int> node=linkedList.Find(5);//找不到返回為null
//在某個節(jié)點之后添加一個節(jié)點
linkedList.AddAfter(node,15);
//在某個節(jié)點之后前添加一個節(jié)點
linkedList.AddBefore(node,12);
//判斷某一元素是否存在
if(linkedList.Contains(5))
{
//鏈表中存在5
}
//改
//找到某一節(jié)點
//改變其數(shù)值
//找到某個節(jié)點
LinkedListNode<int> node1=linkedList.Find(5);//找不到返回為null
node1.Value=15;
//遍歷
foreach(int item in linkedList)
{
//打印節(jié)點
Console.WriteLine(item);
}
//從頭節(jié)點進行遍歷查找
LinkedListNode<int> curNode=linkedList.First;
while(curNode!=null)
{
Console.WriteLine(curNode.Value);
curNode=curNode.Next;
}
//從尾部節(jié)點進行遍歷查找
LinkedListNode<int> curNode=linkedList.Last;
while(curNode!=null)
{
Console.WriteLine(curNode.Value);
curNode=curNode.PreVious;
}
泛型棧和隊列
Stack<int> stack;
Queue<int> queue;
具體的api和非泛型的相同。不再贅述!
委托與事件
委托
委托是函數(shù)的容器,可以理解為表示函數(shù)的變量類型,用來存儲、傳遞函數(shù)。
委托的本質(zhì)是一個類,用來定義函數(shù)的類型(返回值和參數(shù)的類型)
不同的函數(shù)必須對應(yīng)各自“格式”的委托。
關(guān)鍵字:delegate
存在位置:namespace 中(一般在此處),class中
//委托
//帕斯卡命名法
delegate void MyFuns();//無參無返回值的委托(此類型函數(shù)的家)
delegate int MyFuns2(int a);//不可以重名?。?!盡管類型不同也不可以
//默認為public的,一般不用private
class Program
{
static void Main(string[] args)
{
//將Fun函數(shù)放入委托容器中
MyFuns funs=new MyFuns(Fun);
//調(diào)用委托(中的函數(shù))
funs.Invoke();
//======或者
MyFuns f2=Fun;//聲明委托
f2();//調(diào)用委托
//============================
MyFuns2 funs2=new MyFuns2(Fun2);
funs2.Invoke(2);
MyFuns2 f2=Fun2;//聲明
f2(2);//調(diào)用
}
static void Fun()
{
Console.WriteLine("我是個函數(shù)!");
}
static void Fun4()
{
Console.WriteLine("我是個函數(shù)Fun4!");
}
static int Fun2(int a)
{
Console.WriteLine("我是另外一種類型函數(shù)");
return a*2;
}
}
委托變量是函數(shù)的容器:
委托常用在:
- 作為類的成員
- 作為函數(shù)的參數(shù)
//接上一個代碼塊
class Test
{
public MyFuns funs;
public MyFuns2 funs2;
//委托作為函數(shù)的參數(shù)
public void TestFun(MyFuns funs,MyFuns2 funs2)
{
//先處理一些邏輯
//邏輯 code
//······
//再執(zhí)行委托方法
this.funs=funs;
this.funs2=funs2;
}
}
//在上個代碼塊Main中調(diào)用
//使用
Test t=new Test();
t.TestFun(Fun,Fun2);//傳遞的函數(shù)名字
委托中存儲多個函數(shù)(多播委托)
//多播委托
MyFuns funs3=null;
fun3+=Fun;
fun3+=Fun4;
fun3.Invoke();//執(zhí)行
//本質(zhì):觀察者模式====
//老板來了!可以執(zhí)行委托,通知存儲在委托中的員工好好工作的函數(shù)執(zhí)行。
//老板走了!可以執(zhí)行另一種委托,通知存儲在委托中的員工開始摸魚。(狗頭)
//委托輕松實現(xiàn)!
小點:委托中多次減去同一個函數(shù),不會報錯。
委托執(zhí)行之前最好要檢查是否為空!
系統(tǒng)提供的委托:
//系統(tǒng)定義好的委托
//Action是無參無返回值類型的委托
Action action=Fun;
action+=Fun4;
action();//執(zhí)行
//可以傳參數(shù)無返回值的委托
Action<int> ac1;
Action<int,float> ac2;
//=========Action無返回值委托===========
//=========Func有返回值=================
//Func委托
Func<int> funcInt;//返回值為int 無參數(shù)的委托
Func<int,float> func2;//參數(shù)為int 返回值為float的委托
funcInt+=Funs;
static int Fun5()
{
Console.WriteLine("我是函數(shù)Fun5");
return 5;
}
//自定義泛型委托
delegate T MyFun3<T>(T value);
相當(dāng)于給每個裝在進委托中的函數(shù)來一次單獨的分配一個新的委托,各自的函數(shù)都存入自己獨立的委托,然后調(diào)用單獨的委托便會執(zhí)行在委托中的函數(shù)。(對foreach遍歷的理解)
委托練習(xí):
大耳朵圖圖一家吃飯!
使用托通知大家吃飯。
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();
}
}
}
事件
事件是基于委托的存在,是安全的委托。
事件的使用:
- 事件是作為成員變量存在于子類中
- 其它用法和委托相同
事件和委托的不同之處:事件不能在類外部賦值(但可以進行+=,-=)和調(diào)用。(事件必須在類中賦值和使用)
注意:它只能作為成員存在于類和接口以及結(jié)構(gòu)體中。
class Test
{
//委托
public Action myAction;
//事件
public event Action myEvent;
}
為什么要用事件呢?
- 防止外部隨意的置空委托
- 防止外部隨意調(diào)用委托
- 事件對委托進行一次的封裝,使用更安全
匿名函數(shù)
匿名函數(shù)沒有函數(shù)名字,往往都配合事件和委托使用的。
基本語法:
delegate(參數(shù)列表)``{
//函數(shù)邏輯
};
何時使用:
- 函數(shù)中傳遞委托參數(shù)時
- 委托或事件賦值時候
//匿名函數(shù)
//無參 無返回
Action action = delegate()
{
//匿名函數(shù)的聲明
//匿名函數(shù)聲明,放置在委托容器中
Console.WriteLine("我是匿名函數(shù)");
};
action();//調(diào)用委托時候 調(diào)用匿名函數(shù)
//有參 無返回值
Action<int,string> action2=delegate(int a,string b)
{
Console.WriteLine(a);
Console.WriteLine(b);
};//注意:匿名函數(shù)最后加分號
action2(100,"Tony");
//有返回值 無參數(shù)
Func<int> action3=delegate()
{
return 666;
};
action3();
//一般情況下會作為函數(shù)參數(shù)傳遞
class Test
{
public Action action;
//作為參數(shù)
public void DoSomething(int a,Action ac)
{
Console.WriteLine(a);
ac();
}
//作為返回值
public void GetFun()
{
return delegate(){
//無參數(shù)無返回值的匿名函數(shù)
//作為函數(shù)的返回值
};
}
}
//使用
Test t=new Test();
t.DoSomething(100,delegate(){
Console.WriteLine("隨函數(shù)傳入的匿名函數(shù)邏輯");
});
//接受返回的匿名函數(shù)
Action ac3=t.GetFun();
//執(zhí)行
ac3();
//或者一步到位
t.GetFun()();
//匿名函數(shù)的缺點:
//匿名函數(shù)添加到委托中,因為沒有名字,不方便進行管理。
//不能指定移除某個匿名函數(shù)
Lambda表達式
lambda只是匿名函數(shù)的簡寫形式,本質(zhì)還是匿名函數(shù)
//Lambda表達式
//無參無返回值
Action action=()=>{
Console.WriteLine("無參無返回值的");
};
//執(zhí)行
action();
//====有參數(shù) 無返回值
Action action2=(int value)=>
{
Console.WriteLine("有參無返回值的Lambda{0}",value);
};
//調(diào)用執(zhí)行
action2(666);
//可以省略參數(shù)類型,參數(shù)類型和存儲其的容器(事件或委托)來判斷
Action<int> action3=(value)=>{
Console.WriteLine("省略了參數(shù)類型的Lambda表達式的寫法{0}",value);
};
action(999);
//======有返回值=======
Func<string,int> action4=(value)=>{
Console.WriteLine("有參有返回值的Lambda表達式{0}",value);
return 666;
};
int a=action4("hhh");
閉包:
內(nèi)層函數(shù)可以引用包含在它外層函數(shù)的變量,即便外層函數(shù)的執(zhí)行已經(jīng)終止。
注意:該變量的值并非創(chuàng)建變量時候的初始值,而是在外層函數(shù)處理過的最終值。
//例如
static Func<int,int>TestFun(int i)
{
return delegate(int v)
{
//內(nèi)部函數(shù)占用著外部函數(shù)的i
return i*v;
}
}
//該變量的值并非創(chuàng)建變量時候的初始值,而是在外層函數(shù)處理過的最終值。
class Test
{
public event Action action;
public Test()
{
int value=66;
action=()=>
{
Console.WriteLine("占用外部的value");
};
//再次給事件中添加匿名函數(shù)
for(int i=0;i<10;i++)
{
action+=()=>{
//此時所有的i
//為外部函數(shù)執(zhí)行完循環(huán)之后的最終值
//最終值i=10;
Console.WriteLine(i);
};
}
//此時action中共有個匿名函數(shù)
//第一個函數(shù)中value=66
//其余for循環(huán)添加的匿名函數(shù)參數(shù)i的值均為10
//對比:
//再次給事件中添加匿名函數(shù)
for(int i=0;i<10;i++)
{
int index=i;
action+=()=>{
//此時所有的i
//為外部函數(shù)執(zhí)行完循環(huán)之后的最終值
//最終值i=10;
Console.WriteLine(index);
};
}
//第一個函數(shù)中value=66
//中間的for循環(huán)添加的匿名函數(shù)參數(shù)i的值均為10
//最后的for循環(huán)添加的匿名函數(shù)參數(shù)index的值則是0~9
//因為添加的是index臨時變量,index的數(shù)值就是自身最終數(shù)值 而i還在for
//循環(huán)中自增
}
}
List排序
//List排序
List<int> 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類如何排序? 拆箱比較?
//自定義類排序
//若想使用排序函數(shù) 要實現(xiàn)一個排序接口
class Item:IComparable<Item>
{
public int money;
public Item(int money)
{
this.money=money;
}
//排序規(guī)則函數(shù)
//List.Sort()根據(jù)存儲元素的方法來做排序
public int CompareTo(Item other)
{
//返回值>0往后排
//返回值=0保持不變
//返回值<0往前移
if(this.money>other.money)
{
return 1;//this對象移動到other對象后面
}else
{
return -1;//this對象移到other對象前面
}
}
}
List<Item> itemList=new List<Item>();
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();//自定義排序規(guī)則調(diào)用排序
//小理解:List類在調(diào)用Sort排序時候,會將元素類型 as為排序接口類型(里氏替換原則)
//然后調(diào)用其CompareTo方法進行比較。
//3 通過委托函數(shù)進行排序
class ShopItem
{
public int id;
public ShopItem(int id)
{
this.id=id;
}
}
List<ShopItem> shopItems=new List<ShopItem>();
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;
}
}
//調(diào)用Sort排序
//使用Sort的帶有委托參數(shù)的重載函數(shù)
shopItems.Sort(SortShopItem);
//使用匿名內(nèi)部類
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;
});
//遍歷結(jié)果
for(int i=0;i<shopItems.Count;i++)
{
Console.WriteLine(shopItems[i].id);
}
協(xié)變和逆變
協(xié)變:和諧的變化。例如,里氏替換原則,父類可以裝子類,因此子類變父類是一種和諧的變化。string變成object(裝箱)也是協(xié)變。(可以理解小變大,順應(yīng)形勢,理所應(yīng)當(dāng)。)
逆變:于協(xié)變相反,逆常規(guī)的變化,不正常的變化,子類裝父類,object變string就屬于協(xié)變。(可以理解大變小,強制轉(zhuǎn)換,扭扭捏捏)
協(xié)變和逆變是用來修飾泛型的,只有在泛型接口和泛型委托中修飾泛型字符。
關(guān)鍵字:
協(xié)變:out(順勢而為,順出)修飾的只能作為 返回值
逆變:in(逆流而上,逆進)修飾的只能作為 參數(shù)
//協(xié)變和逆變
//out修飾類型 作為返回值
delegate T TestFunOut<out T>();
//in修飾的泛型 只能作為參數(shù)
delegate void TestFunIn(in T)(T t);
結(jié)合里氏替換原則:
class Father
{
}
class Son:Father
{
}
class Program
{
static void Main(string[] args)
{
TestFunOut<Son> os=()=>{
return new Son();
};
TestFunOut<Father> of=os;//父類型的委托裝載子類型的委托,二者都是委托的返回值
//符合out 協(xié)變 如果沒有用out修飾,則說明二者是不同返回值類型的
//委托,不可以進行賦值。
TestFunIn<Father> InF=(value)=>
{
};
TestFunIn<Son> InS=InF;
Ins(new Son());
//子類型的委托接受父類型的委托,但二者都是屬于委托參數(shù),可以理解為,
//子類型的構(gòu)造函數(shù)調(diào)用父類型的構(gòu)造函數(shù)來構(gòu)造,底層本質(zhì)還是符合大裝小的原則
//in修飾之后,才可以進行大小委托賦值。是一種逆變
}
}
多線程
進程?
進程是運行中的程序,系統(tǒng)進行資源分配和調(diào)度的基本單位,是操作系統(tǒng)的基礎(chǔ)。
線程?
線程存在于進程中,是進程中的實際運作單位,是操作系統(tǒng)能夠進行運算調(diào)度的最小單位。一個進程中可以并發(fā)多個線程,他們共享利用進程中的堆棧資源。
多線程?
一個進程中除了必要的主線程之外,可以開啟其它的線程來完成一些計算處理。
一個進程中除了必要的主線程之外,可以開啟其它的線程來完成一些計算處理。
//線程
//聲明
Thread t=new Thread(MyThreadFun);
bool isRunning=true;
static void MyThreadFun()
{
while(isRunning)
{
Thread.Sleep(10000);
Console.WriteLine("新線程的執(zhí)行函數(shù)");
}
}
//啟動
t.Start();
//后臺線程
//設(shè)置為后臺線程之線程受主線程的影響(主線程結(jié)束,其它線程也結(jié)束),后臺線程不會防止應(yīng)用程序進程被終止
//如果不設(shè)置為后臺線程可能會導(dǎo)致進程無法正常關(guān)閉
t.IsBackground= true;
//關(guān)閉線程
//如果線程中有死循環(huán)一致執(zhí)行的,要關(guān)閉線程
//主動結(jié)束死循環(huán)
//對while()判斷條件控制設(shè)為false
isRunning=false;
//或者通過對線程提供的方法
//不一定都通用
t.Abort();
t=null;
//線程休眠
Thread.Sleep(10000);
//多線程--線程鎖
//避免對一個資源同時操作會造成邏輯錯誤
//多線程中要使用lock 保證一個內(nèi)存區(qū)一刻時間只能有一個線程訪問
Object obj=new Object();
//鎖的使用引用類型
lock(obj)
{
//邏輯
}
預(yù)處理器指令
預(yù)處理是在源程序編譯成目標(biāo)指令之前,可以簡單的幫助選擇一些條件是否要編譯執(zhí)行。
常見的預(yù)處理指令:
#define Tony
#define Lili
#if Tony
Console.WriteLine("Tony");
#elseif
#endif Lili
Console.WriteLine("Lili");
#else
Console.WriteLine("其他人");
#undef Tony
#waring
#error
反射和特性
反射:
程序正在運行時候,可以查看其它程序集或者自身的元數(shù)據(jù)。一個運行的程序查看本身或者其它程序的元數(shù)據(jù)的行為就叫反射。
我們知道,程序=數(shù)據(jù)+算法,程序在執(zhí)行過程中需要不斷地給它“投喂”數(shù)據(jù)養(yǎng)料,而反射就是獲取養(yǎng)料(類、函數(shù),變量等)的過程。有了反射,在運行時候可以動態(tài)的獲取自身所需要的資源,可以提高程序的拓展性和靈活性。
程序集:
程序集是經(jīng)由編譯器編譯得到的,供進一步編譯執(zhí)行的那個中間產(chǎn)物。在WINDOWS系統(tǒng)中,它一般表現(xiàn)為后綴為·dll(庫文件)或者是·exe(可執(zhí)行文件)的格式。
也就是說程序集是我們開發(fā)的項目中程序的集合,工程文件編譯運行所需要執(zhí)行的所有文件資源都算作程序集中的內(nèi)容。
元數(shù)據(jù):
元數(shù)據(jù)就是用來描述數(shù)據(jù)的數(shù)據(jù),例如在程序中,類、函數(shù)、變量等等信息就是程序的元數(shù)據(jù),她們保存在程序集中。
Type:
它是反射功能的基礎(chǔ),訪問元數(shù)據(jù)的主要方式.
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 < infos.Length; i++)
{
Console.WriteLine(infos[i]);
}
//獲取所有的構(gòu)造函數(shù)
ConstructorInfo[] ctors=type.GetConstructors();
for(int i=0;i<ctors.Length;i++)
{
Console.WriteLine(ctors[i]);
}
//獲取其中一個構(gòu)造函數(shù)并執(zhí)行
//獲取無參構(gòu)造函數(shù)
ConstructorInfo info=type.GetConstructor(new Type[0]);
//執(zhí)行無參構(gòu)造
Test obj=info.Invoke(null) as Test;//無參構(gòu)造 所以要傳null
//得到一個參數(shù)的有參構(gòu)造
ConstructorInfo info2=type.GetConstructor(new Type[]{typeof(int)});
obj = info2.Invoke(new object[]{5}) as Test;
//獲取兩個參數(shù)的構(gòu)造函數(shù)
ConstructorInfo info3=type.GetConstructor(new Type[]{typeof(int),typeof(string)});
obj = info2.Invoke(new object[]{5,"Tony"}) as Test;//與獲取的類型參數(shù)類型匹配
//獲取類的公共成員變量
FieldInfo[] fieldInfos=type.GetFields();
for (int i = 0; i < fieldInfos.Length; i++)
{
Console.WriteLine(fieldInfos[i]);
}
//得到指定名稱的公共成員變量
//獲取J
FieldInfo infoJ=type.GetField("j");
Console.WriteLine(infoJ);
//通過反射獲取和設(shè)置對象的數(shù)值
Test test=new Test();
test.j=99;
test.name="Tony";
//通過反射獲取某個對象的數(shù)值
infoJ.GetValue(test);
//設(shè)置某個對象的數(shù)值
infoJ.SetValue(test,100);
//=================
//獲取類的公共成員方法
//以string類為例
Type strType=typeof(string);
MethodInfo[] methods=strType.GetMethods();
for(int i=0;i<methods.Length;i++)
{
Console.WriteLine(methods[i]);
}
//如果存在方法重載 使用type數(shù)組來表示參數(shù)類型
MethodInfo subStr=strType.GetMethod("Substring",new Type[]{typeof(int),tyoeof(int)});
//調(diào)用獲取到的方法
string str="Hello,TonyChang";
object result=subStr.Invoke(str,new object[]{5,9});
Console.WriteLine(result);
//還可以通過
//GetEnumName GetEnumNames得枚舉
//得事件
//GetEvent
//GetEvents
//得接口
//GetInterface
//GetInterfaces
//得屬性
//GetProperty
//GetPropertys
//===============Activator=====
//可以使用它來快捷實例化Type類型得對象
Type testType=typeof(Test);
Test testObj=Activator.CreateInstance(testType) as Test;//無參構(gòu)造
testObj=Activator.CreateInstance(testType,99) as Test;//有參構(gòu)造
testObj=Activator.CreateInstance(testType,99,"Tony") as Test;//有參構(gòu)造(參數(shù)類型要對應(yīng))
//==============Assembly(程序集類)================
//主要用來加載其它程序集 加載之后用Type來使用其內(nèi)容
//可以加載dll文件等
//三種加載程序集的函數(shù)
//一般用來加載在同一文件下的其它程序集
Assembly asembly2 = Assembly.Load("程序集名稱");
//一般用來加載不在同一文件下的其它程序集
Assembly asembly = Assembly.LoadFrom("包含程序集清單的文件的名稱或路徑");
Assembly asembly3 = Assembly.LoadFile("要加載的文件的完全限定路徑");
//1.先加載一個指定程序集
Assembly asembly = Assembly.LoadFrom(@"C:\Users\MECHREVO\Desktop\CSharp");
Type[] types = asembly.GetTypes();
//遍歷打印
for (int i = 0; i < types.Length; i++)
{
Console.WriteLine(types[i]);
}
//2.再加載程序集中的一個類對象 之后才能使用反射
Type icon = asembly.GetType("Lesson18_練習(xí)題.Icon");
MemberInfo[] members = icon.GetMembers();
for (int i = 0; i < members.Length; i++)
{
Console.WriteLine(members[i]);
}
//通過反射 實例化一個 icon對象
//首先得到枚舉Type 來得到可以傳入的參數(shù)
Type moveDir = asembly.GetType("Lesson18_練習(xí)題.E_MoveDir");
FieldInfo right = moveDir.GetField("Right");
//直接實例化對象
object iconObj = Activator.CreateInstance(icon, 10, 5, right.GetValue(null));
//得到對象中的方法 通過反射
MethodInfo move = icon.GetMethod("Move");
MethodInfo draw = icon.GetMethod("Draw");
MethodInfo clear = icon.GetMethod("Clear");
//進行調(diào)用程序
Console.Clear();
while(true)
{
Thread.Sleep(1000);
clear.Invoke(iconObj, null);
move.Invoke(iconObj, null);
draw.Invoke(iconObj, null);
}
//注:調(diào)用得程序是繪制方塊的練習(xí)題
特性
本質(zhì)是個類,可以對元數(shù)據(jù)的解釋和說明,供反射獲取的時候來獲取被調(diào)用的元數(shù)據(jù)的信息。
系統(tǒng)生成的特性:
過時特性:
迭代器
迭代器iterator
有時也稱為光標(biāo),是一種的軟件設(shè)計模式,迭代器模式提供一個方法順序訪問一個聚合對象中的各個元素,并且不暴露其內(nèi)部的標(biāo)識。
實現(xiàn)了迭代器的類才可以使用foreach遍歷。
//迭代器
//繼承兩個接口實現(xiàn)
//光標(biāo)移動
//迭代器
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 };
}
//實現(xiàn)IEnumerable的接口方法
public IEnumerator GetEnumerator()
{
Reset();//返回之前要將索引器回到0
return this;
}
//=========================
public object Current
{
get => list[position];
}
//移動光標(biāo)
public bool MoveNext()
{
++position;
return position < list.Length;
}
//重置光標(biāo)
public void Reset()
{
position = -1;
}
}
//遍歷??!Main方法中
MyList list = new MyList();
foreach(int item in list)
{
Console.WriteLine(item);
}
試著理解foreach的本質(zhì):
- 先獲取要遍歷對象的IEnumerator,調(diào)用其中的GetEnumerator方法獲取
- 執(zhí)行IEnumerator對象中的MoveNext方法
- 若返回為true,則繼續(xù)調(diào)用Current獲取得到值給item
使用語法糖實現(xiàn)迭代器文章來源:http://www.zghlxwxcb.cn/news/detail-711509.html
//迭代器
class MyList1:IEnumerable
{
private int[] list;
public MyList1()
{
list = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
}
//實現(xiàn)IEnumerable的接口方法
public IEnumerator GetEnumerator()
{
for(int i=0;i<list.Length;i++)
{
//使用語法糖
//yield return 是C#提供給我們的語法糖
//所謂語法糖,也稱糖衣語法
//主要作用就是將復(fù)雜邏輯簡單化,可以增加程序的可讀性
//從而減少程序代碼出錯的機會
//要保留當(dāng)前狀態(tài),暫時返回出去
yield return list[i];
//其它兩個相關(guān)函數(shù)由系統(tǒng)自動生成
}
}
//=========================
}
//遍歷!!Main方法中
MyList1 list1 = new MyList1();
foreach(int item in list1)
{
Console.WriteLine(item);
}
使用語法糖為泛型數(shù)組實現(xiàn)迭代器文章來源地址http://www.zghlxwxcb.cn/news/detail-711509.html
//泛型實現(xiàn)迭代器類
class MyList<T> : IEnumerable
{
private T[] array;
public MyList(params T[] array)
{
this.array = array;
}
public IEnumerator GetEnumerator()
{
for (int i = 0; i < array.Length; i++)
{
yield return array[i];
}
}
}
到了這里,關(guān)于C#學(xué)習(xí)筆記--數(shù)據(jù)結(jié)構(gòu)、泛型、委托事件等進階知識點的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!