十一、C#入門基礎11(委託,事件,反射)
一、委託
C# 中的委託(Delegate)類似於 C 或 C++ 中函式的指標。
委託(Delegate) 是存有對某個方法的引用的一種引用型別變數。
引用可在執行時被改變。
委託(Delegate)特別用於實現事件和回撥方法。
所有的委託(Delegate)都派生自 System.Delegate 類。
宣告委託
委託宣告決定了可由該委託引用的方法。
委託可指向一個與其具有相同標籤的方法。
delegate
例項化委託
一旦聲明瞭委託型別,委託物件必須使用 new 關鍵字來建立,且與 一個特定的方法有關。
當建立委託時,傳遞到 new 語句的引數就像方法呼叫一樣書寫,但 是不帶有引數。
二、事件
事件(Event) 基本上說是一個使用者操作,如按鍵、點選、滑鼠移動等等,或 者是一些出現,如系統生成的通知。應用程式需要在事件發生時響應事件。例 如,中斷。事件是用於程序間通訊。
事件的概念:
通過事件使用委託
事件在類中宣告且生成,且通過使用同一個類或其他類中的委託與事件處理程式關聯。
包含事件的類用於釋出事件。這被稱為釋出器(publisher) 類。
其他接受該事件的類被稱為訂閱器(subscriber) 類。
事件使用釋出-訂閱(publisher-subscriber) 模型。
釋出器(publisher)是一個包含事件和委託定義的物件。
事件和委託之間的聯絡也定義在這個物件中。
釋出器(publisher)類的物件呼叫這個事件,並通知其他的物件。
訂閱器(subscriber) 是一個接受事件並提供事件處理程式的物件。
在釋出器(publisher)類中的委託呼叫訂閱器(subscriber)類中的方法(事件處理程式)。
宣告事件:
在類的內部宣告事件,首先必須宣告該事件的委託型別。
宣告事件本身,使用 event 關鍵字
實現的程式碼(委託,事件):
Hello類(釋出器):
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _11 { public class Hello { /// <summary> /// 定義好事件的委託 /// </summary> public delegate void ShowHiDelegate(); /// <summary> /// 定義好一個你好的事件 /// </summary> public event ShowHiDelegate ShowHievent; /// <summary> /// 執行問好的方法 /// </summary> public void ClickShowHievent() { if (ShowHievent!=null) { //執行問好的方法 ShowHievent(); } } } }
Jp類(訂閱器):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _11
{
/// <summary>
/// 日本人
/// </summary>
public class Jp
{
public void ShowHi()
{
Console.WriteLine("日本人說你好");
}
}
}
USA類(訂閱器):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _11
{
public class USA
{
public void ShowHi()
{
Console.WriteLine("美國人說你好");
}
}
}
Program類(實現的類):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _11
{
class Program
{
/// <summary>
/// 可以將方法作為引數進行傳遞的特殊的引用型別變數
/// 實現方法的模板->抽象類 介面 (針對於方法)
/// 實現委託的方法必須和委託定義的是一模一樣 (不能有任何改變 引數的名稱可以不一樣)
/// </summary>
//宣告一個委託
//delegate void ShowHi(string str);
static void Main(string[] args)
{
//ShowHi sh = new ShowHi(ShowHi2);
//sh("zs");
//委託是C#中的靈魂 從軟體開發的角度講我們應該瞭解整個系統的流程(業務邏輯)
//開發應該瞭解底層 底層:怎麼走的怎麼來的
//加事件用 += 取消事件 -=
//實際上事件的執行的規則和過程與委託一致
//事件最終執行的內容依然是委託
//事件和委託的關聯是因為 在定義事件的時候就會有一個委託的定義 並且事件的型別 就是當前的委託
//1 需要例項化你的釋出器
Hello h = new Hello();
//2 需要例項化你的訂閱器
Jp j = new Jp();
USA u = new USA();
//3 給釋出器加上訂閱器
h.ShowHievent += new Hello.ShowHiDelegate(j.ShowHi);
h.ShowHievent += new Hello.ShowHiDelegate(u.ShowHi);
//4 給釋出器去除訂閱器
h.ShowHievent -= new Hello.ShowHiDelegate(u.ShowHi);
//執行事件
h.ClickShowHievent();
Console.ReadKey();
}
//public static void ShowHi2(string str1)
//{
// Console.WriteLine("你好"+str1);
// Console.ReadKey();
//}
}
}
三、反射
反射指程式可以訪問、檢測和修改它本身狀態或行為的一種能力。
程式集包含模組,而模組包含型別,型別又包含成員。
反射則提供了封裝程式集、模組和型別的物件。
您可以使用反射動態地建立型別的例項,將型別繫結到現有物件,或從現有物件中獲取型別。
然後,可以呼叫型別的方法或訪問其欄位和屬性。
優點:
1、反射提高了程式的靈活性和擴充套件性。
2、降低耦合性,提高自適應能力。
3、它允許程式建立和控制任何類的物件,無需提前硬編碼目標 類。
缺點:
1、效能問題:使用反射基本上是一種解釋操作,用於欄位和方 法接入時要遠慢於直接程式碼。因此反射機制主要應用在對靈 活性和拓展性要求很高的系統框架上,普通程式不建議使 用。
2、使用反射會模糊程式內部邏輯;程式設計師希望在原始碼中看到 程式的邏輯,反射卻繞過了原始碼的技術,因而會帶來維護 的問題,反射程式碼比相應的直接程式碼更復雜。
反射的用途:
1.使用Assembly定義和載入程式集,載入在程式集中的所有模 塊以及從此程式集中查詢型別並建立該型別的例項。
2.使用Module瞭解包含模組的程式集以及模組中的類等,還可 以獲取在模組上定義的所有全域性方法或其他特定的非全域性 方法。
3.使用ConstructorInfo瞭解建構函式的名稱、引數、訪問修飾 符(如pulic 或private)和實現詳細資訊(如abstract或 virtual)等。
4.使用MethodInfo瞭解方法的名稱、返回型別、引數、訪問修 飾符(如pulic 或private)和實現詳細資訊(如abstract或 virtual)等。
5.使用FiedInfo瞭解欄位的名稱、訪問修飾符(如public或 private)和實現詳細資訊(如static)等,並獲取或設定欄位 值。
6.使用EventInfo瞭解事件的名稱、事件處理程式資料型別、自 定義屬性、宣告型別和反射型別等,新增或移除事件處理 程式。
7.使用PropertyInfo瞭解屬性的名稱、資料型別、宣告型別、 反射型別和只讀或可寫狀態等,獲取或設定屬性值。
8.使用ParameterInfo瞭解引數的名稱、資料型別、是輸入引數 還是輸出引數,以及引數在方法簽名中的位置等。
反射用到的主要類:
1.System.Type 類
通過這個類可以訪問任何給定資料型別的資訊。
2.System.Reflection.Assembly 類
它可以用於訪問給定程式集的資訊,或者把這個程式集載入 到程式中。
System.Type類用法:
System.Type類對於反射起著核心的作用。但它是一個抽象的基類,Type有與每種資料型別對應的派生類,我們使用這個派生類的物件的方法、欄位、屬性來查詢有關該型別的所有資訊。獲取給定型別的Type引用有3種常用方式:
1.使用 C# typeof 運算子
2.使用物件GetType()方法
3.呼叫Type類的靜態方法GetType(“類的全路徑”)
Type類的屬性:
1.Name資料型別名
2.FullName 資料型別的完全限定名(包括名稱空間名)
3.Namespace 定義資料型別的名稱空間名
4.IsAbstract 指示該型別是否是抽象型別
5.IsArray 指示該型別是否是陣列
6.IsClass 指示該型別是否是類
7.IsEnum 指示該型別是否是列舉
8.IsInterface 指示該型別是否是介面
9.IsPublic 指示該型別是否是公有的
10.IsSealed 指示該型別是否是密封類
11.IsValueType 指示該型別是否是值型別
Type類的方法:
1.GetConstructor(), GetConstructors():
返回ConstructorInfo型別,用於取得該類的建構函式的資訊
2.GetEvent(), GetEvents():
返回EventInfo型別,用於取得該類的事件的資訊
3.GetField(), GetFields():
返回FieldInfo型別,用於取得該類的欄位(成員變數)的 資訊
4.GetInterface(), GetInterfaces():
返回InterfaceInfo型別,用於取得該類實現的介面的資訊
5.GetMember(), GetMembers():
返回MemberInfo型別,用於取得該類的所有成員的資訊
6.GetMethod(), GetMethods():
返回MethodInfo型別,用於取得該類的方法的資訊
7.GetProperty(), GetProperties():
返回PropertyInfo型別,用於取得該類的屬性的資訊
System.Reflection.Assembly類的用法:
Assembly類可以獲得程式集的資訊,也可以動態的載入程式集,以及在程式集中查詢型別資訊,並建立該型別的例項。使用Assembly類可以降低程式集之間的耦合性,有利於軟體結構的合理化。
1.通過程式集名稱返回Assembly物件
Assembly.Load(“類名”);
2.通過DLL檔名稱返回Assembly物件 Assembly.LoadFrom(@“c:\ReflectionDemo2.dll”);
3.通過Assembly獲取程式集中類
Assembly物件名.GetType(“引數必須是類的全名”);
4.通過Assembly獲取程式集中所有的類
Assembly物件名.GetTypes();
實現的程式碼:
Person類:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _11_1
{
public class Person
{
//欄位
private int _pid;
public string _pname;
//屬性
public int Pid
{
get
{
return _pid;
}
set
{
_pid = value;
}
}
public string Pname
{
get
{
return _pname;
}
set
{
_pname = value;
}
}
//public Person() { }
//public Person(int _pid, string _pname)
//{
// this.Pid = _pid;
// this.Pname = _pname;
//}
/// <summary>
/// 定義好事件的委託
/// </summary>
public delegate void ShowHiDelegate();
/// <summary>
/// 定義好一個你好的事件
/// </summary>
public event ShowHiDelegate ShowHievent;
public void PersonShow(string str)
{
Console.WriteLine("人"+str);
}
}
}
Program類(實現的類):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace _11_1
{
class Program
{
static void Main(string[] args)
{
//Person p = new Person();
//使用物件GetType()方法
//Type t = p.GetType();
//呼叫Type類的靜態方法GetType("類的全路徑") 像java class.fromname
//Type t = Type.GetType("_11_1.Person");
//資料型別名
//Console.WriteLine(t.Name);
//資料型別的完全限定名(包括名稱空間名)
//Console.WriteLine(t.FullName);
//定義資料型別的名稱空間名
//Console.WriteLine(t.Namespace);
//指示該型別是否是抽象型別
//Console.WriteLine(t.IsAbstract);
//指示該型別是否是陣列
//Console.WriteLine(t.IsArray);
//指示該型別是否是類
//Console.WriteLine(t.IsClass);
//指示該型別是否是列舉
//Console.WriteLine(t.IsEnum);
//指示該型別是否是介面
//Console.WriteLine(t.IsInterface);
//指示該型別是否是公有的
//Console.WriteLine(t.IsPublic);
//指示該型別是否是密封類
//Console.WriteLine(t.IsSealed);
//指示該型別是否是值型別
//Console.WriteLine(t.IsValueType);
//Console.ReadKey();
//Type t = Type.GetType("_11_1.Person");
//object objects =Activator.CreateInstance(t);
//1.返回ConstructorInfo型別,用於取得該類的建構函式的資訊
//Type[] types = new Type[0];
//ConstructorInfo c=t.GetConstructor(types);
//Console.WriteLine(c.IsPublic);
//2.返回EventInfo型別,用於取得該類的事件的資訊
//EventInfo[] ei= t.GetEvents();
//foreach (EventInfo e in ei)
//{
// Console.WriteLine(e.Name);
//}
//3.返回FieldInfo型別,用於取得該類的欄位(成員變數)的資訊
//FieldInfo fi = t.GetField("_pname");
//Person p=new Person(); p.setpname="zs";
//fi.SetValue(objects,"zs");
//Console.WriteLine(fi.GetValue(objects));
//4.返回MemberInfo型別,用於取得該類的所有成員的資訊
//MemberInfo[] mb=t.GetMember("ShowHiDelegate");
//foreach (MemberInfo m in mb)
//{
// Console.WriteLine(m.Name);
//}
//5.返回MethodInfo型別,用於取得該類的方法的資訊
//MethodInfo[] mi=t.GetMethods();
//foreach (MethodInfo item in mi)
//{
// Console.WriteLine(item.Name);
//}
//6.返回PropertyInfo型別,用於取得該類的屬性的資訊
//PropertyInfo pi =t.GetProperty("Pname");
//set_Pname
//pi.SetValue(objects,"zs");
//get_Pname
//Console.WriteLine(pi.GetValue(objects));
//反射呼叫方法
//MethodInfo mi=t.GetMethod("PersonShow");
//object[] oo =new object[1];
//oo[0] = "zs";
//mi.Invoke(objects,oo);
//Assembly as1=Assembly.Load("Person");
Assembly as1 = Assembly.LoadFrom(@"D:\Visual Studio 2015\Projects\217Base\11\bin\Debug\11.dll");
Type[] t=as1.GetTypes();
foreach (Type item in t)
{
Console.WriteLine(item.Name);
}
Console.ReadKey();
}
}
}