什么是委托?
委托是一个类型安全的对象,它指向程序中另一个以后会被调用的方法(或多个方法)。通俗的说,委托是一个可以引用方法的对象,当创建一个委托,也就创建一个引用方法的对象,进而就可以调用那个方法,即委托可以调用它所指的方法。
来看下面的例子,类deleMthod定义了3个方法,add、minus和multi,他们都具有相同的输入参数列表(int x,int y)和输出参数类型int,那么我们就说这三个方法具有相同的方法签名。开发者可以抽象地用 int 某名称(int x,int y) 的一种类型对方法进行封装,在c#中这种抽象的数据类型叫委托,针对上述的几个方法我们可以定义委托 : public delegate int Handler(int x ,int y),public 是一个访问修饰符,delegate关键字表示这是一个委托,int Hander(int x,int y)表示这个委托的名称。
class deleMethod
{
public int add(int x, int y)
{
return x + y;
}
public int minus(int x, int y)
{
return x - y;
}
public int multi(int x, int y)
{
return x * y;
}
}
怎么使用委托
使用委托大体有四个步骤:
•定义一个委托,上节已经提及。
•定义委托方法,上节deleMethod类中add、minus、multi都是委托方法,定义的目的就是为了使用它,讲专业点就是为了方法的调用
•委托变量及赋值,和类型一样,在使用前需要对变量赋值。
•委托变量的使用。
怎样定义委托变量,还是接着上面的例子。我们已经定义了一个委托类型 public delegate int Handler(int x,int y),和c#语法规范一样定义一个变量并赋值语法是:“类型名 变量名 = new 类型名(方法);”,如上例
“Handler deleCall = new Handler(方法名);“,在.net2.0后对于委托的实例化可以简化为” Handler deleCall = 方法名;“。
委托变量的使用,也就是对委托方法的调用。其语法是”int result1 = deleCall(10,20);“或者使用委托调用方法 Invoke,“int result2 = deleCall.Invoke(10,20);”。
具体如何使用可以看看下面的示例:
class Program
{
public delegate int Handler(int x, int y); //---定义委托的类型,可以将委托看成一种特殊的数据类型
static void Main(string[] args)
{
deleMethod dm = new deleMethod(); //---实例化包含委托方法的类型
Handler deleCall = new Handler(dm.add); //---定义委托变量delCall,并出示化赋值
int result1 = deleCall(10, 20); //---实例方法的调用invoke
Console.WriteLine("the add resutl is:{0}", result1);
deleCall = dm.minus;
int result2 = deleCall.Invoke(12, 6);
Console.WriteLine("the minus result is:{0}", result2);
Console.ReadLine();
}
}
如上例所示,定义一个简单的加、减功能如此的复杂,搅来搅去让人头,真是无语,难怪很多朋友谈委托色变晕。在实际使用的过程中,c#还是有很多方式帮我们简化代码。
简化委托
预定义的泛型委托
c#系统最常见的预定义的委托类型有三种,Func<>委托、Action<>委托、Predicate<>委托,Func<>委托是一个有返回值的委托,输入参数可以多达16个;而Action<>委托是一个没有返回值的委托,它的输入参数也可以多达16个;而Predicate<>是一个具有bool返回类型的委托,它只运行一个输入参数。对于有上例的委托类型,我们可以使用预定义的委托类型Fun<int,int,int>来代替,省去我们自己定义一个什么鬼东西 public delegate int Handler(int x,int y)类型,其代码其实可以简化为如下例所示:
namespace DelegateDemo1
{
class Program
{
static void Main(string[] args)
{
deleMethod dm = new deleMethod();
Func<int, int, int> fun = dm.add; //---使用预定义的委托类型Func<>
int result4 = fun(8, 10);
Func<int, int, int> fun1 = dm.minus;
int result5 = fun1(12, 8);
Console.WriteLine("预定义的委托输出{0},{1}", result4, result5);
Console.ReadLine();
}
}
class deleMethod
{
public int add(int x, int y)
{
return x + y;
}
public int minus(int x, int y)
{
return x - y;
}
public int multi(int x, int y)
{
return x * y;
}
}
}
我把委托的方法定义和委托的调用放在一起看,是不是比原先自己定义的一个委托类型简单方便一些?但是这样使用委托还是不怎么清爽,估计在实际应用中很少人会怎么写代码,太不方便了。
匿名委托
当委托实例的调用和委托方法的定义分开处理,码农们在读程序代码的时候需要来回的去找委gt;
/// 主程序类
/// </summary>
class Program
{
static void Main(string[] args)
{
eventPublish myEvent = new eventPublish();
eventSubscription myMethod = new eventSubscription();
//绑定方法 add 和 subtract,又称为对事件方法的注册 -=称为对事件方法的注销
myEvent.calc += myMethod.add;
myEvent.calc += myMethod.substract;
while (true)
{
try
{
Console.WriteLine("请输入第一个整数数");
int numA = Convert.ToInt16(Console.ReadLine());
Console.WriteLine("请输入第二个整数");
int numB = Convert.ToInt16(Console.ReadLine());
calcEventArgs e = new calcEventArgs(numA, numB);
//在本例不需要sender参数,随意传递了字符串"sender"
myEvent.onCalc("sender", e);
}
catch (Exception ex)
{
Console.WriteLine("出现错误," + ex.Message);
}
}
}
}
/// <summary>
/// 定义一个事件发布器类
/// </summary>
class eventPublish
{
//定义一个委托类型,委托名一般是 事件变量名+EventHandler
public delegate void calcEventHander(object sender,calcEventArgs e);
//定义一个事件变量,其变量名为calc
public event calcEventHander calc;
//封装事件,对外定义了引发事件的方法,定义的引发方法名一般是 on+事件变量名
public void onCalc(object sender, calcEventArgs e)
{
if (calc != null)
calc(sender,e);
}
}
/// <summary>
/// 定义一个事件订阅者类(事件方法类)
/// </summary>
class eventSubscription
{
public void add(object sender, calcEventArgs e)
{
Console.WriteLine("两数相加等于{0}", e.X + e.Y );
}
public void substract(object sender, calcEventArgs e)
{
Console.WriteLine("两数相减等于{0}", e.X - e.Y);
}
}
/// <summary>
/// 定义一个事件参数类
/// </summary>
class calcEventArgs : EventArgs
{
private int _x;
private int _y;
public calcEventArgs(int x, int y)
{
this._x = x;
this._y = y;
}
public int X
{
get { return _x; }
}
public int Y
{
get { return _y; }
}
}
我们将事件是对委托的封装,如果用ILDAS反编译可执行文件查看中间代码就可以非常明了的看出事件运行机制
如上图所示,事件calc,经.net运行时编译为中间语言后产生了一个private 的calc属性,同时也自动生成了add_calc和remove_calc方法,用于注册和注销订阅者方法。因为事件是被封装的,尽管calc属性是private,但在所以在发布器类内部可以用 calc(sender,e)这样的方法直接调用;但在主程序中如果这样使用就会出错,只能通过onCalc方法进行间接调用。
后记
本篇文章,一开始提出了什么是委托的疑问,通过引入几个方法来讲述委托是什么以加强对委托概念的理解。第二部分讲述了使用委托的四个步骤,并通过示例阐述了这几个步骤。第三部分讲述了委托使用的简化问题,通过使用泛型委托简化自定义委托,通过使用匿名委托可以简化定义委托方法。匿名委托是在定义委托的时候直接给出方法体,通过使用lambda表达式的类型推断可进一步简化委托的使用。第四部分讲述了委托链,通过绑定方法初始化委托,并通过+=绑定更多的委托方法。第五部分讲述了事件的定义和使用的四个步骤。当然委托的使用场景还有很多,比如通过BeginInvoke和EndInvoke进行异步调用,因不是本篇文章的重点,所以文章中没有提及。
以上内容是小编给大家介绍的C#中的委托数据类型简介,希望对大家有所帮助! |