委托Delegate
C#中的Delegate对应于C中的指针,但是又有所不同C中的指针既可以指向方法,又可以指向变量,并且可以进行类型转换,
C中的指针实际上就是内存地址变量,他是可以直接操作内存的,通过内存地址直接访问变量,直接调用方法。
而C#中的Delegate是强类型的,也就是说在声明委托时就已经指定了该变量只能指向具有特定参数,以及返回值的方法。
使用delegate就可以直接建立任何名称的委托类型,当进行系统编译时,系统就会自动生成此类型。您可以使用delegate void MyDelegate()
方式建立一个委托类,并使用ILDASM.exe观察其成员。由ILDASM.exe 中可以看到,它继承了System.MulticastDelegate类,
并自动生成BeginInvoke、EndInvoke、Invoke 等三个常用方法。
Invoke 方法是用于同步调用委托对象的对应方法,而BeginInvoke、EndInvoke是用于以异步方式调用对应方法的。
public class MyDelegate:MulticastDelegate
{
//同步调用委托方法
public virtual void Invoke();
//异步调用委托方法
public virtual IAsyncResult BeginInvoke(AsyncCallback callback,object state);
public virtual void EndInvoke(IAsyncResult result);
}
MulticastDelegate是System.Delegate的子类,它是一个特殊类,编译器和其他工具可以从此类派生,但是自定义类不能显式地从此类进行派生。它支持多路广播委托,并拥有一个带有链接的委托列表,在调用多路广播委托时,系统将按照调用列表中的委托出现顺序来同步调用这些委托。
MulticastDelegate具有两个常用属性:Method、Target。其中Method 用于获取委托所表示的方法Target 用于获取当前调用的类实例。
委托的使用
当建立委托对象时,委托的参数类型必须与委托方法相对应。只要向建立委托对象的构造函数中输入方法名称example.Method,委托就会直接绑定此方法。使用myDelegate.Invoke(string message),就能显式调用委托方法。但在实际的操作中,我们无须用到 Invoke 方法,而只要直接使用myDelegate(string message),就能调用委托方法。
无返回值的委托
class Program
{
delegate void MyDelegate(string message);
public class Example
{
public void Method(string message)
{
MessageBox.Show(message);
}
}
static void Main(string[] args)
{
Example example=new Example();
MyDelegate myDelegate=new MyDelegate(example.Method);
myDelegate("Hello World");
}
}
有返回值的委托
class Program
{
delegate string MyDelegate(string message);
public class Example
{
public string Method(string name)
{
return "Hello " + name;
}
}
static void Main(string[] args)
{
Example example=new Example();
//绑定委托方法
MyDelegate myDelegate=new MyDelegate(example.Method);
//调用委托,获取返回值
string message = myDelegate("Leslie");
Console.WriteLine(message);
}
}
多路广播委托
delegate double MyDelegate(double message);
public class Price
{
public double Ordinary(double price)
{
double price1 = 0.95 * price;
Console.WriteLine("Ordinary Price : "+price1);
return price1;
}
public double Favourable(double price)
{
double price1 = 0.85 * price;
Console.WriteLine("Favourable Price : " + price1);
return price1;
}
static void Main(string[] args)
{
Price price = new Price();
//绑定Ordinary方法
MyDelegate myDelegate = new MyDelegate(price.Ordinary);
//绑定Favourable方法
myDelegate += new MyDelegate(price.Favourable);
//调用委托
Console.WriteLine("Current Price : " + myDelegate(100));
}
}
泛型委托
public class Worker { int wages; public int Wages { get { return wages; } set { wages = value; } } } public class Manager : Worker { } class Program { public delegate void Handler(T obj); public static void GetWorkerWages(Worker worker) { Console.WriteLine("Worker's total wages is " + worker.Wages); } public static void GetManagerWages(Manager manager) { Console.WriteLine("Manager's total wages is " + manager.Wages); } static void Main(string[] args) { HandlerworkerHander = new Handler(GetWorkerWages); Worker worker = new Worker(); worker.Wages = 3000; workerHander(worker); HandlermanagerHandler = new Handler(GetManagerWages);
Manager manager = new Manager();
manager.Wages = 4500;
managerHandler(manager);
}
}
事件
产生原因
事件是特殊的委托,他为委托提供了封装性,一方面允许从类的外部增加,删除绑定方法,另一方面又不允许从类的外部来触发委托所绑定了方法。
public delegate double PriceHandler();
public class PriceManager
{
public PriceHandler GetPriceHandler;
//委托处理,当价格高于100元按8.8折计算,其他按原价计算
public double GetPrice()
{
if (GetPriceHandler.GetInvocationList().Count() > 0)
{
if (GetPriceHandler() > 100)
return GetPriceHandler()*0.88;
else
return GetPriceHandler();
}
return -1;
}
}
class Program
{
static void Main(string[] args)
{
PriceManager priceManager = new PriceManager();
//调用priceManager的GetPrice方法获取价格
//直接调用委托的Invoke获取价格,两者进行比较
priceManager.GetPriceHandler = new PriceHandler(ComputerPrice);
Console.WriteLine(string.Format("GetPrice\n Computer's price is {0}!",
priceManager.GetPrice()));
Console.WriteLine(string.Format("Invoke\n Computer's price is {0}!",
priceManager.GetPriceHandler.Invoke()));
Console.WriteLine();
priceManager.GetPriceHandler = new PriceHandler(BookPrice);
Console.WriteLine(string.Format("GetPrice\n Book's price is {0}!",
priceManager.GetPrice()));
Console.WriteLine(string.Format("Invoke\n Book's price is {0}!" ,
priceManager.GetPriceHandler.Invoke()));
Console.ReadKey();
}
//书本价格为98元
public static double BookPrice()
{
return 98.0;
}
//计算机价格为8800元
public static double ComputerPrice()
{
return 8800.0;
}
}
以上代码实现了对于100元以上商品的的88折处理。一方面为了给GetPriceHandler绑定方法就必须将委托声明为public,但是一旦声明为public
就可以在类外部直接通过Invoke来调用该委托所绑定的方法,而产生我们不需要的结果
当然我们可以将GetPriceHandler声明为private并且通过public 的addHandler,removeHandler来消除委托public的副作用,但是C#提供了更加优雅的方法:
那就是event关键字。
事件(event)可被视作为一种特别的委托,它为委托对象隐式地建立起add_XXX、remove_XXX 两个方法,用作注册与注销事件的处理方法。而且事件对应的变量成员将会被视为 private 变量,外界无法超越事件所在对象直接访问它们,这使事件具备良好的封装性,而且免除了add_XXX、remove_XXX等繁琐的代码。
public class EventTest
{
public delegate void MyDelegate();
public event MyDelegate MyEvent;
}
观察事件的编译过程可知,在编译的时候,系统为 MyEvent 事件自动建立add_MyEvent、remove_MyEvent 方法。
使用
事件能通过+=和-=两个方式注册或者注销对其处理的方法,使用+=与-=操作符的时候,系统会自动调用对应的 add_XXX、remove_XXX 进行处理。
值得留意,在PersonManager类的Execute方法中,如果 MyEvent 绑定的处理方法不为空,即可使用MyEvent(string)引发事件。但如果在外界的 main 方法中直接使用 personManager.MyEvent (string) 来引发事件,系统将引发错误报告。这正是因为事件具备了良好的封装性,使外界不能超越事件所在的对象访问其变量成员。
注意:在事件所处的对象之外,事件只能出现在+=,-=的左方。
public delegate void MyDelegate(string name);
public class PersonManager
{
public event MyDelegate MyEvent;
//执行事件
public void Execute(string name)
{
if (MyEvent != null)
MyEvent(name);
}
}
class Program
{
static void Main(string[] args)
{
PersonManager personManager = new PersonManager();
//绑定事件处理方法
personManager.MyEvent += new MyDelegate(GetName);
personManager.Execute("Leslie");
}
public static void GetName(string name)
{
Console.WriteLine("My name is " + name);
}
}
在绑定事件处理方法的时候,事件出现在+=、-= 操作符的左边,对应的委托对象出现在+=、-= 操作符的右边。对应以上例子,事件提供了更简单的绑定方式,只需要在+=、-= 操作符的右方写上方法名称,系统就能自动辩认。
public delegate void MyDelegate(string name);
public class PersonManager
{
public event MyDelegate MyEvent;
.........
}
class Program
{
static void Main(string[] args)
{
PersonManager personManager = new PersonManager();
//绑定事件处理方法
personManager.MyEvent += GetName;
.............
}
public static void GetName(string name)
{.........}
}
如果觉得编写 GetName 方法过于麻烦,你还可以使用匿名方法绑定事件的处理。
public delegate void MyDelegate(string name);
public class PersonManager
{
public event MyDelegate MyEvent;
//执行事件
public void Execute(string name)
{
if (MyEvent != null)
MyEvent(name);
}
static void Main(string[] args)
{
PersonManager personManager = new PersonManager();
//使用匿名方法绑定事件的处理
personManager.MyEvent += delegate(string name){
Console.WriteLine("My name is "+name);
};
personManager.Execute("Leslie");
}
}
lamda表达式
在Framework 2.0 以前,声明委托的唯一方法是通过方法命名,从Framework 2.0 起,系统开始支持匿名方法。
通过匿名方法,可以直接把一段代码绑定给事件,因此减少了实例化委托所需的编码系统开销。
而在 Framework 3.0 开始,Lambda 表达式开始逐渐取代了匿名方法,作为编写内联代码的首选方式。总体来说,Lambda 表达式的作用是为了使用更简单的方式来编写匿名方法,彻底简化委托的使用方式。
使用匿名方法
public delegate void MyDelegate(string name);
public class PersonManager
{
public event MyDelegate MyEvent;
.........
}
static void Main(string[] args)
{
PersonManager personManager = new PersonManager();
//使用匿名方法绑定事件的处理
personManager.MyEvent += delegate(string name){
Console.WriteLine("My name is "+name);
};
}
使用lambda表达式
static void Main(string[] args)
{
PersonManager personManager = new PersonManager();
//使用lambda表达式
personManager.MyEvent += (name) =>{
Console.WriteLine("My name is "+name);
};
}
常用泛型委托:
Action此委托由系统提供 无需声明Action 支持0~16个参数,可以按需求任意使用。public delegate void Action()public delegate void Action(T1 obj1)public delegate void Action(T1 obj1, T2 obj2)public delegate void Action(T1 obj1, T2 obj2,T3 obj3)............public delegate void Action(T1 obj1, T2 obj2,T3 obj3,......,T16 obj16)static void Main(string[] args) { int x = 1000; Action action = () => x = x + 500; action.Invoke(); Console.WriteLine("Result is : " + x); }static void Main(string[] args) { Action action = (x) =>
{
x = x + 500;
Console.WriteLine("Result is : " + x);
};
action(1000);
}
委托 Func 与 Action 相似,同样支持 0~16 个参数,不同之处在于Func 必须具有返回值public delegate TResult Func()public delegate TResult Func(T1 obj1)public delegate TResult Func(T1 obj1,T2 obj2)public delegate TResult Func(T1 obj1,T2 obj2,T3 obj3)............public delegate TResult Func(T1 obj1,T2 obj2,T3 obj3,......,T16 obj16)static void Main(string[] args) { Funcfunc = Account; double result=func(1000, true); Console.WriteLine("Result is : "+result); } static double Account(double a,bool condition) { if (condition) return a * 1.5; else return a * 2; }static void Main(string[] args){ Func func = (a,condition) =>
{
if (condition)
{
return a * 1.5;
}
else
{
return a * 2;
}
};
double result=func(1000, true);
Console.WriteLine("Result is : "+result);
}