委托的定义与方法的定义类似,只是返回值类型的前面多了个delegate。如:public delegate void PrientDelegate(string content),其实委托就是一个能够将方法封装成一个引用类型,能和普通对象一样传递。
一、委托声明的注意点
1、和类一样,委托的声明可以再类的外部声明,委托也可以再类的内部声明。
2、委托的声明虽然形式上与方法很像,但是它没有方法主体,而是直接以分号结尾。
3、修饰符可以是new、public、protected、internal、private。
4、delegate关键字后是本委托类型可以匹配的方法签名,尤其需要注意的是,它还包括方法的返回类型,但并非与方法名完全匹配。
二、给委托绑定方法时,要符合的两点要求
1、方法的签名要与委托一样,方法参数的个数和类型。
2、方法的返回值类型要与委托一样。
调用委托就相当于调用委托所绑定的方法, 一个委托可以绑定多个方法。委托是引用类型,因此它就可以到处传递,没有它,C#能够将方法到处传递吗?
先附上一个基础的例子
public delegate void Print(string str); //声明一个委托
class Program
{
public static void PrintString(string str)
{
Console.WriteLine(str);
}
static void Main(string[] args)
{
//创建一个委托对象,并将方法PrintString赋给该委托
Print p = new Print(Program.PrintString);
//Print p = Program.PrintString; //直接用=号也可以让委托绑定方法。
p("调用委托实际等于调用了委托所绑定的方法!"); //输出调用委托实际等于调用了委托所绑定的方法!
Console.ReadKey();
}
}
三、委托的组合(多个委托之间的关系)
"+"可以将两个委托组合起来,"-"可以从一个委托组合中删除其中一个成员。
public delegate void Print();
class Program
{
public static void PrintString1()
{
Console.WriteLine("我是第一个方法");
}
public static void PrintString2()
{
Console.WriteLine("我是第二个方法");
}
static void Main(string[] args)
{
//创建一个委托对象并绑定方法
Print p1 = Program.PrintString1; //委托p1绑定方法1
Print p2 = Program.PrintString2; //委托p2绑定方法2
Print p3 = p1 + p2; //将p1委托和p2委托组合成一个新委托p3
p3(); //调用委托p3,相当于调用p3所有已经绑定的方法,此时是方法1与方法2,此时输出 我是第一个方法 "换行" 我是第二个方法
Print p4 = p3 - p1; //创建一个新的委托对象,其绑定的方法为p3委托组合去除委托p1,实际就只剩下p2
p4(); //调用委托p4,相当于调用所有p4绑定的方法 输出 我是第二个方法
Console.ReadKey();
}
}
四、委托的添加方法与删除方法(一个委托的事情)
通过"+="符可以像已有委托新添加绑定一个方法,通过"-="可以从委托中删除一个已绑定的方法。
public delegate void Print();
class Program
{
public static void PrintString1()
{
Console.WriteLine("我是第一个方法");
}
public static void PrintString2()
{
Console.WriteLine("我是第二个方法");
}
static void Main(string[] args)
{
Print p = PrintString1; //声明一个委托并绑定第一个方法
p(); //调用委托p 输出 我是第一个方法
p += PrintString2; //再为p多绑定一个方法
p(); //调用委托p 输出我是第一个方法 "换行" 我是第二个方法,看到现在p委托一经是绑定两个方法的了
p -= PrintString1; //从委托p中删除一个已经绑定的方法p1;
p(); //调用委托p 输出 我是第二个方法 看到p现在已经是只绑定第二个方法了
Console.ReadKey();
}
}
五、委托在回调中的应用
委托在回调函数中的调用:
public delegate void PrintCompleteCallback(string message);
class Program
{
public static void PrintMessage(string message)
{
Console.WriteLine(message);
}
static void Main(string[] args)
{
PrintCompleteCallback CompleteCallback = Program.PrintMessage; //创建一个委托,并绑定PrintMessage方法
Printer printer = new Printer("我是一个打印机");
printer.Print(CompleteCallback); //调用p对象的Print方法,要求传入一个委托作为参数,相当于传入一个方法作为参数
Console.ReadKey();
}
}
public class Printer
{
private string _name;
public Printer(string name)
{
_name = name;
}
public void Print(PrintCompleteCallback callback) //委托对象作为方法参数
{
callback(_name + "打印完毕"); //调用委托,相当于调用callback委托所绑定的对象
}
}
六、匿名方法
匿名方法最大的优势在于其减少了系统开销,方法仅在委托使用时才定义。直接给委托传递一段代码比先创建一个方法再绑定到委托要简单。
匿名方法的使用规则:
1、匿名方法中不能使用跳转语句跳至此匿名方法的外部,反之亦然;匿名方法外部的跳转语句也不能跳到此匿名方法的内部。
2、在匿名方法内部不能访问不安全的代码。另外,也不能访问在匿名方法外部定义的ref与out参数。但可以使用在匿名方法外部定义的其它变量。
public delegate void Print(); //定义一个委托
public delegate int GetLength(string str); //定义一个带参数的委托
class Program
{
static void Main(string[] args)
{
Print p = delegate //不带参数的匿名方法
{
Console.WriteLine("你在他乡还好吗?");
};
p();
GetLength g = delegate(string str) //带参数的匿名方法,注意方法的参数要与委托一致
{
return str.Length;
};
int length = g("你还好吗?"); //调用委托,注意要传入参数
Console.WriteLine(length);
Console.ReadKey();
}
}
七、Lambda表达式
Lambda表达式和匿名方法基本相同,只是语法不同而已,可以说,Lambda表达式就是匿名方法。必须要记住,Lambda就是一个匿名方法,是给委托赋值用的,也就是说,使用Lambda的场景只有给委托赋值。
Lambda表达式的语法更加简洁:
(param)=>expr //其中param是一个输出参数列表,说白了就是给委托的参数,expr是一个表达式或一系列语句,说白了就是输出或执行的操作
Lambda表达式具有以下特性:
1、在一个具有唯一的显式类型参数的Lambda表达式中,圆括号可以从参数列表中删除。例如:param=>expr
2、当输入参数不唯一时,括号不能省略。
3、输入参数列表中的各参数可以显式指定类型,也可以省略掉参数类型,具体类型通过类型推断机制判断。
4、expr可以只包含一个计算表达式,也可以包含一系列语句,只是如果是一系列语句必须包括在大括号内。
一个委托参数的Lambda表达式
public delegate string Print(string content); //定义一个委托,该委托接受一个字符串输入,用于Lambda的左边
class Program
{
static void Main(string[] args)
{
Print p = (str) => str += "-输出参数"; //(str) 输入参数,也就是委托参数中的content,箭头后的是输出,最简单的理解就是,箭头左边是输入,箭头左边是输出,当然语句就不同了。
string outstr = p("输入参数");
Console.WriteLine(outstr); //输出 输入参数-输出参数
Console.ReadKey();
}
}
两个委托参数的Lambda表达式的调用:
public delegate string Print(string content1,string content2); //定义一个委托
class Program
{
static void Main(string[] args)
{
Print p = (str1,str2) => str1 += "左边-右边" + str2; //多个输入参数,括号不能省略
string outstr = p("张飞","关羽"); //输出 张飞左边-右边关羽
Console.WriteLine(outstr); //输出 输入参数-输出参数
Console.ReadKey();
}
}
带语句的Lambda表达式
public delegate void Print(string content); //定义一个委托
class Program
{
static void Main(string[] args)
{
Print p = str => //带语句的Lambda表达式,由于只有一个参数,所以小括号可以省略
{
Console.WriteLine(str);
};
p("你好"); //你好
Console.ReadKey();
}
}
2014-04-08-----------------------------
现在学完了Lambda表达式了,可以结合扩展方法来瞧瞧Linq是怎么实现的了。下面写个模仿Linq的Where实现的示例:
namespace ConsoleApplication3
{
public delegate bool Compare(int i);
class Program
{
static void Main(string[] args)
{
//.Net的Linq扩展的是IEnumerable<TSource>(底层,范围广),而我是Calculate类
Calculate c = new Calculate();
int[] intArr = new int[] { 10, 20 };
int[] intArrReturn = c.Where(m => m > 18, intArr);
foreach (int i in intArrReturn)
{
Console.WriteLine(i);
}
Console.ReadKey();
}
}
public static class Exten
{
//技术点1:扩展方法,扩展的是Calculate类(第一个参数)
//技术点2:以委托作为参数,在方法内部会调用(第二个参数)
public static int[] Where(this Calculate c, Compare compare, int[] intArr)
{
int[] returnArr = new int[2]; //示例定长数组
int count = 0;
foreach (int i in intArr)
{
if (compare(i))
{
returnArr[count] = i;
count++;
}
}
return returnArr;
}
}
//计算类
public class Calculate
{
}
}
有那么点像了,Linq是泛型+Lambda,所以它的名字比较帅,如果我们把委托(参数名)改一下:Compare改为Func<T>
public delegate bool Func<T>(int i);
看下代码提示,是不是更像Linq了?

很遗憾,到目前为止,我们扩展的Calculate类不支持链式操作,因为返回的结果是一个int[]数组。而链式操作的原理是,返回的结果又能继续,如IEnumerable<int>的Where(m => m.xx)之后返回的又是IEnumerable<int>,所以能够无限下去。
2014-04-08-----------------------------------
八、委托中的协变与逆变
协变:委托方法的返回值类型直接或间接地继承自委托签名的返回值类型,称为协变。
逆变:委托签名中的参数类型继承自委托方法的参数类型,称为逆变。
注意:协变强调的是返回类型,逆变强调的则是方法参数。
协变Demo:
delegate People getSon(string name);
class Program
{
static void Main(string[] args)
{
getSon g = sons;
People p = g("张飞"); //注意虽然委托方法返回的是Son,但是委托本身返回的是父类People
Console.WriteLine(p.Name); //输出 张飞
Console.ReadKey();
}
public static Son sons(string name) //委托方法的返回值类型继承自委托签名的返回值类型 逆变
{
Son m = new Son();
m.Name = name;
return m;
}
}
public class People
{
public People() { }
public int Id { get; set; }
public string Name { get; set; }
}
public class Son : People
{
public int Age { get; set; }
}
逆变Demo:
delegate string getSonName(Son s);
class Program
{
static void Main(string[] args)
{
getSonName g = sons;
Son s = new Son();
s.Name = "关羽";
string str = g(s);
Console.WriteLine(str); //输出 关羽
Console.ReadKey();
}
public static string sons(People p) //委托签名的参数类型继承自委托方法的参数类型 此People为委托签名参数Son的父类
{
return p.Name;
}
}
public class People
{
public People() { }
public int Id { get; set; }
public string Name { get; set; }
}
public class Son : People
{
public int Age { get; set; }
}

浙公网安备 33010602011771号