C# Delegates 委托
通常我们都是把数据作为参数传递给方法:int i = int.Parse(“99”);当需要把方法传送给其他方法时就需要使用委托。
类的用法,首先声明一个类,接着实例化一个类,委托的用法和类的用法类似,首先定义委托告诉编译器这种类型的委托表示哪种类型的方法,接着创建该委托的一个或者多个实例。
声明委托
委托的类型安全性非常高,在定义委托时必须给出他所表示的方法的签名和返回值,例如声明委托如下:
delegete void IntMethodInvoker(int x); 该方法带有一个int型参数,返回void。
假设需要定义一个委托,该委托表示的方法有两个long类型的参数,返回值为double:
delegete double TwoLongOp (long l1,long l2);
根据定义委托的可见性和委托的作用域,可以在委托的定义上应用任意常见的访问修饰符:public,private,protected。
使用委托
创建委托的实例:
IntMethodInvoker intMethod = null;
TwoLongOp twoLongOp = null;
namespace Delegates
{
class Program
{
// 声明委托
private delegate string GetString();
static void Main(string[] args)
{
// 定义一个委托的实例getString
GetString getString = null;
int i = 99;
// 给委托赋值
getString = i.ToString;
Console.WriteLine($"{getString()}");
}
}
}
在给委托赋值时,这个方法必须匹配委托声明时的签名,在调用时使用了类似方法的getString(),C#编译器会用getString.Invoke()代替getString()。
Action< T >和Func< T >委托
泛型Action< T >委托表示引用一个void返回类型的方法。Action< T >可以调用8个参数的方法。
泛型Func< T >委托允许调用返回类型的方法。Fun< T >至多可以传递16个参数类型和一个返回类型,例如:Func<int T,out T>。
class Program
{
private delegate double DoubleOp(double x);
static void Main(string[] args)
{
DoubleOp[] operations = { MathOpertions.MuliplyByTwo,MathOpertions.Squate};
for(int i = 0; i < operations.Length; i++)
{
ProcessAndDisplayNumble(operations[i], 2.0);
ProcessAndDisplayNumble(operations[i], 7.94);
ProcessAndDisplayNumble(operations[i], 1.414);
}
Console.WriteLine($"");
}
static void ProcessAndDisplayNumble(DoubleOp action, double value)
{
double result = action(value);
Console.WriteLine($"value={value},operation result ={result}");
}
}
class MathOpertions
{
public static double MuliplyByTwo(double value) => value * 2;
public static double Squate(double value) => value * value;
}
上面列举了一个普通的委托实例,需要先声明一个委托,下面用Fun< double T , double T >修改上面的代码,不需要声明委托。
static void Main(string[] args)
{
Func<double,double>[] operations = { MathOpertions.MuliplyByTwo,MathOpertions.Squate};
for(int i = 0; i < operations.Length; i++)
{
ProcessAndDisplayNumble(operations[i], 2.0);
ProcessAndDisplayNumble(operations[i], 7.94);
ProcessAndDisplayNumble(operations[i], 1.414);
}
Console.WriteLine($"");
}
static void ProcessAndDisplayNumble(Func<double, double> action, double value)
{
double result = action(value);
Console.WriteLine($"value={value},{action.ToString()} result ={result}");
}
}
class MathOpertions
{
public static double MuliplyByTwo(double value) => value * 2;
public static double Squate(double value) => value * value;
}
多播委托
以上每个委托都只包含一个方法调用,调用委托的次数和调用 方法的次数相同。委托可以包含多个方法,如果调用多播委托,就会按照顺序连续调用多播委托中的方法。多播委托可以识别‘+’、‘+=’、‘-’、‘-=’。
class Program
{
static void Main(string[] args)
{
Action<double> operations = null;
operations = MathOpertions.MuliplyByTwo;
operations += MathOpertions.Square;
double[] arr = new double[]{ 2.0, 7.94, 1.414 };
foreach(var item in arr)
{
operations(item);
}
Console.WriteLine($"Hello World");
}
}
class MathOpertions
{
public static void MuliplyByTwo(double value)
{
double result = value * 2;
Console.WriteLine($"MuliplyByTwo value={value} reult={result}");
}
public static void Square(double value)
{
double result = value * 2;
Console.WriteLine($"Square value={value} reult={result}");
}
}
使用多播委托会有一个问题:通过委托调用的其中一个方法抛出异常,整个迭代就会停止。如下代码所示,在方法中假装抛出异常:
class Program
{
static void Main(string[] args)
{
Action<double> operations = null;
operations = MathOpertions.MuliplyByTwo;
operations += MathOpertions.Square;
double[] arr = new double[]{ 2.0, 7.94, 1.414 };
foreach(var item in arr)
{
operations(item);
}
Console.WriteLine($"Hello World");
}
}
class MathOpertions
{
public static void MuliplyByTwo(double value)
{
double result = value * 2;
Console.WriteLine($"MuliplyByTwo value={value} reult={result}");
throw new Exception("this is a Exception");
}
public static void Square(double value)
{
double result = value * 2;
Console.WriteLine($"Square value={value} reult={result}");
}
}
解决这个问题:使用try…catch,抛出异常后循环继续
class Program
{
static void Main(string[] args)
{
Action<double> operations = null;
operations = MathOpertions.MuliplyByTwo;
operations += MathOpertions.Square;
double[] arr = new double[]{ 2.0, 7.94, 1.414 };
foreach(var item in arr)
{
try
{
operations(item);
}catch(Exception e)
{
Console.WriteLine(e.Message);
}
}
Console.WriteLine($"Hello World");
}
}
class MathOpertions
{
public static void MuliplyByTwo(double value)
{
double result = value * 2;
Console.WriteLine($"MuliplyByTwo value={value} reult={result}");
throw new Exception("this is a Exception");
}
public static void Square(double value)
{
double result = value * 2;
Console.WriteLine($"Square value={value} reult={result}");
}
}
虽然循环继续,但是每次只执行第一个方法,因为第一个方法抛出异常后委托的迭代会停止。解决这个问题,需要自己迭代方法列表:
class Program
{
static void Main(string[] args)
{
Action<double> operations = null;
operations = MathOpertions.MuliplyByTwo;
operations += MathOpertions.Square;
Delegate[] delegates = operations.GetInvocationList();
double[] arr = new double[]{ 2.0, 7.94, 1.414 };
foreach(var item in arr)
{
foreach(Action<double> d in delegates)
{
try
{
d(item);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
Console.WriteLine($"Hello World");
}
}
class MathOpertions
{
public static void MuliplyByTwo(double value)
{
double result = value * 2;
Console.WriteLine($"MuliplyByTwo value={value} reult={result}");
throw new Exception("this is a Exception");
}
public static void Square(double value)
{
double result = value * 2;
Console.WriteLine($"Square value={value} reult={result}");
}
}