用什么软件做购物网站,京东商城官网入口,承德网站建设专家,企业如何建站委托 简介 委托是一种可以声明出指向方法的变量的数据类型。 声明委托的方式 格式#xff1a; delegate 返回值类型 委托类型名(参数) #xff0c;例如#xff1a; delegate void MyDel(string str) 。
// 注意#xff1a;这里除了前面的 delegate 关键字#xff…委托 简介 委托是一种可以声明出指向方法的变量的数据类型。 声明委托的方式 格式 delegate 返回值类型 委托类型名(参数) 例如 delegate void MyDel(string str) 。
// 注意这里除了前面的 delegate 关键字剩下部分和声明一个函数相同但是 MyDel 不是函数名而是委托类型名。 创建委托类型变量 声明委托变量的方式与声明变量相同都是通过 new 关键字例 MyDel sayHello new MyDel(SayHello);
/** SayHello 是一个方法句柄并且它的返回值需要与 MyDel 的参数返回值相同* sayHello 这个委托变量就指向 SayHello 方法*/ 还有一种简化的写法 MyDel sayHello SayHello;
/** 反编译查看如下* MyDel sayHello new MyDel(SayHello);* 即其实与原始写法相同*/ 委托的使用 要使用委托可以直接使用 委托变量名() 的方式调用委托指向的方法如果有参数就传递参数例 using System;
using NUnit.Framework;namespace MyTests
{[TestFixture]public class Tests{delegate void MyDel(string str);void SayHello(string name){Console.WriteLine(hello name);}[Test]public void Test(){MyDel sayHello SayHello;sayHello(张三);/** hello 张三*/}}
} MyTests.Tests 委托变量之间可以互相赋值其实就是一个传递方法指针的过程如 using System;
using NUnit.Framework;namespace MyTests
{[TestFixture]public class Tests{delegate void MyDel(string str);void SayHello(string name){Console.WriteLine(hello name);}void SayName(string name){Console.WriteLine(name);}[Test]public void Test(){MyDel sayHello SayHello;sayHello SayName; // sayHello 本来指向 SayHello 方法这一行让其指向了 SayName 方法sayHello(张三); // 所以实际执行的是 SayName 方法/** 张三*/}}
} MyTests.Tests 案例一获取最大值 先从一个简单的需求开始如果我们需要编写一个获取 int 数组中最大值的方法很简单如下 1 int GetMaxNum(int[] nums)
2 {
3 int max nums[0];
4 for (var i 1; i nums.Length; i)
5 {
6 if (nums[0] max) max nums[0];
7 }
8 return max;
9 } 假如又有一个要求我们定义一个获取 string 数组中最大值每个 string 变量都可转型为 int 变量的方法显示上述方法就不适用了。那有没有什么方法能够让其通用起来呢 如果我们要获取 string 数组中的最大值显然我们需要先将其中每个元素转换到 int 类型然后再进行比较即重点就是我们要如何定义它的比较规则 上述代码的比较规则是在第 6 行的 if 块中我们要做的就是让那个这个 if 块中的内容动态起来此时委托就派上用场了看如下代码 /*** 获取数组中的最大值*/
object GetMax(object[] nums,CompareFunc compareFunc)
{object max nums[0];for (var i 1; i nums.Length; i){if (compareFunc(nums[i],max))max nums[i];}return max;
}/*** 如果 obj1 比 obj2 大则返回 true否则返回 false*/
delegate bool CompareFunc(object obj1, object obj2); 上述我们新定义了一个 GetMax 方法它的返回值为 object 类型第一个参数为 object 数组类型第二个参数则是 CompareFunc 委托变量。而 CompareFunc 委托的作用就是对比较规则一个定义即我们要做的就是传入对应数组参数的同时也一起传入响应的比较规则的实现。 定义比较规则 /*** 比较规则*/
bool CompareInt(object num1, object num2)
{return Convert.ToInt32(num1) Convert.ToInt32(num2);
} 再看此时我们如何获取 int 数组的最大值 [Test]
public void Test()
{object[] numArr {32, 445, 65, 321, 4};var max GetMax(numArr, CompareInt);Console.WriteLine(max);/** 445*/
} 而我们如果要获取一个 string 数组中的最大值不用做修改直接传入 string 类型数组即可 [Test]
public void Test()
{object[] numArr {32, 445, 65, 321, 4};var max GetMax(numArr, CompareInt);Console.WriteLine(max);/** 445*/
} 此时来了一个新需求有如下实体类 namespace MyTests.Entities
{public class User{public User(){}public User(int id, string name, int age){this.id id;this.name name;this.age age;}private int id;private string name;private int age;public int Id{get { return id; }set { id value; }}public string Name{get { return name; }set { name value; }}public int Age{get { return age; }set { age value; }}public override string ToString(){return string.Format(Id: {0}, Name: {1}, Age: {2}, id, name, age);}}
} MyTests.Entities.User 我们需要定义一个方法能够返回该实体对象数组中年龄最大的对象很简单我们只需要单独为 User 的实例定义一个它的比较规则即可如下 [Test]
public void Test()
{object[] userArr {new User(1, 张三, 34),new User(2, 李四, 23),new User(3, 王五, 34)};var max GetMax(userArr, CompareUser);Console.WriteLine(max);/** Id: 1, Name: 张三, Age: 34*/
}bool CompareUser(object user1, object user2)
{return (user1 as User).Age (user2 as User).Age;
} 委托最大的价值在于可以让我们在编写代码时不用考虑委托变量指向哪一个方法只需要按照声明委托时的约定传入参数即可。其实有点类似于接口的作用我们不需要了解它的具体实现就可以直接使用它。 泛型委托 自定义泛型委托 泛型委托的定义其实与泛型方法的定义相似格式如下 delegate 返回值类型 方法名泛型名称1, 泛型名称2, ...(参数1, 参数2,...); 通过泛型委托上述案例可以修改如下 using System;
using MyTests.Entities;
using NUnit.Framework;namespace MyTests
{[TestFixture]public class GetMaxNumTest{[Test]public void Test(){User[] userArr {new User(1, 张三, 34),new User(2, 李四, 23),new User(3, 王五, 34)};var max GetMaxUser(userArr, CompareUser);Console.WriteLine(max);/** Id: 1, Name: 张三, Age: 34*/}/*** 比较规则*/bool CompareUser(User user1, User user2){return user1.Age user2.Age;}/*** 泛型方法*/T GetMaxT(T[] nums, CompareFuncT compareFunc){T max nums[0];for (var i 1; i nums.Length; i){if (compareFunc(nums[i], max))max nums[i];}return max;}/*** 泛型委托*/delegate bool CompareFuncT(T obj1, T obj2);}
} MyTests.GetMaxNumTest 内置的泛型委托 .Net 中内置两个泛型委托 Func 和 Action 日常开发中基本不用自定义委托类型了。 Func 是有返回值的委托而 Action 是没有返回值的委托。使用如下 using System;
using MyTests.Entities;
using NUnit.Framework;namespace MyTests
{[TestFixture]public class Test1{void SayHello(string name){Console.WriteLine(hello name);}bool CompareUser(User user1, User user2){return user1.Age user2.Age;}[Test]public void Test(){// 无返回值的委托Actionstring sayHello SayHello;sayHello(张三);// 有返回值的委托FuncUser, User, bool compareUser CompareUser;// 如果是有返回值的委托那么最后一个泛型参数为返回值类型var isGt compareUser(new User(1, 张三, 32), new User(2, 李四, 43));Console.WriteLine(isGt);/*hello 张三False*/}}
} MyTests.Test 而上述的案例也可以修改为如下 using System;
using MyTests.Entities;
using NUnit.Framework;namespace MyTests
{[TestFixture]public class GetMaxNumTest2{[Test]public void Test(){User[] userArr {new User(1, 张三, 34),new User(2, 李四, 23),new User(3, 王五, 34)};var max GetMaxUser(userArr, CompareUser);Console.WriteLine(max);/** Id: 1, Name: 张三, Age: 34*/}/*** 比较规则*/bool CompareUser(User user1, User user2){return user1.Age user2.Age;}/*** 泛型方法 使用内置泛型委托*/T GetMaxT(T[] nums, FuncT, T, bool compareFunc){T max nums[0];for (var i 1; i nums.Length; i){if (compareFunc(nums[i], max))max nums[i];}return max;}}
} MyTests.GetMaxNumTest 匿名方法 匿名方法就是没有名字的方法。使用委托的很多时候没必要定义一个普通方法因为这个方法只有这个委托会用并且只用一次这个时候使用匿名方法最为合适。以将 SayHello 方法的指针赋给委托为例 using System;
using NUnit.Framework;namespace MyTests
{[TestFixture]public class Tests{#region 普通方法方式void SayHello(string name){Console.WriteLine(hello name);}public void TestOld(){Actionstring sayHello SayHello;}#endregion#region 匿名方法方式[Test]public void TestNew(){Actionstring sayHello delegate(string name) { Console.WriteLine(hello name); };}#endregion}
} 将最大值哪个案例使用匿名方法重构后如下 using System;
using MyTests.Entities;
using NUnit.Framework;namespace MyTests
{[TestFixture]public class GetMaxNumTest2{[Test]public void Test(){User[] userArr {new User(1, 张三, 34),new User(2, 李四, 23),new User(3, 王五, 34)};// 使用匿名方法var max GetMaxUser(userArr, delegate(User user1, User user2) { return user1.Age user2.Age; });Console.WriteLine(max);/** Id: 1, Name: 张三, Age: 34*/}/*** 泛型方法 使用内置泛型委托*/T GetMaxT(T[] nums, FuncT, T, bool compareFunc){T max nums[0];for (var i 1; i nums.Length; i){if (compareFunc(nums[i], max))max nums[i];}return max;}}
} MyTests.GetMaxNumTest lambda表达式 使用 lambda 表达式其实是对匿名方法使用的一个再度简化看如下示例 Actionstring a1 delegate(string s) { Console.WriteLine(s); }; 上面是将一个匿名方法指针赋值给一个委托变量通过 lambda 表达式可简化如下 Actionstring a2 (string s) { Console.WriteLine(s); }; 还可以省略参数类型编译器会自动根据委托类型推断 Actionstring a3 (s) { Console.WriteLine(s); }; 如果只有一个参数还可以省略小括号 Actionstring a3 s { Console.WriteLine(s); }; 如果委托有返回值并且方法体只有一行代码这一行代码还是返回值那么就可以连方法的大括号和 return 都省略 Funcint,int,int a4 (i, j) i j; 可读作“goes to”。 练习 1、将下面代码尽可能简化 Actionstring, bool a1 delegate(string s, bool b)
{if (b) { Console.WriteLine(true s); }else { Console.WriteLine(false s); }
}; Actionstring, bool a1 (s, b)
{if (b) Console.WriteLine(true s);else Console.WriteLine(false s);
}; result Funcstring, int f1 delegate(string str) { return Convert.ToInt32(str);}; Funcstring, int f1 str Convert.ToInt32(str); result 2、把下面的代码还原成匿名方法形式 Actionstring, int a1 (s, i) { Console.WriteLine(s s ,i i); }; Actionstring, int a1 delegate(string s, int i) { Console.WriteLine(s s ,i i); }; result Funcint, string f2 n (n 1).ToString(); Funcint, string f2 delegate(int n) {return (n 1).ToString();}; result Funcint, int f3 n n * 2; Funcint, int f3 delegate(int n) { return n * 2; }; result 3、写出下面一个 lambda 表达式的委托类型及非匿名函数形式 n n 0; 委托类型为 Funcint, bool
非匿名函数形式
public bool IsGtZero(int n)
{return n 0;
} result 4、使用 lambda 表达式修改获取最大值案例 using System;
using MyTests.Entities;
using NUnit.Framework;namespace MyTests
{[TestFixture]public class GetMaxNumTest2{[Test]public void Test(){User[] userArr {new User(1, 张三, 34),new User(2, 李四, 23),new User(3, 王五, 34)};// 使用匿名方法var max GetMaxUser(userArr, (User user1, User user2) user1.Age user2.Age);Console.WriteLine(max);/** Id: 1, Name: 张三, Age: 34*/}/*** 泛型方法 使用内置泛型委托*/T GetMaxT(T[] nums, FuncT, T, bool compareFunc){T max nums[0];for (var i 1; i nums.Length; i){if (compareFunc(nums[i], max))max nums[i];}return max;}}
} result 案例二扩展集合的Where方法 通过上面学习到的内容我们可以为集合做一个扩展方法这个方法的功能是可以根据我们传入的 lambda 表达式过滤出我们需要的元素集合。 1、编写扩展方法 using System;
using System.Collections.Generic;namespace MyTests.Ext
{/*** 集合扩展方法类*/public static class EnumerableExt{public static IEnumerableT MyWhereT(this IEnumerableT data, FuncT, bool filter){var list new ListT();foreach (var obj in data){if (filter(obj)){list.Add(obj);}}return list;}}
} MyTests.Ext.EnumerableExt 2、使用 User[] userArr
{new User(1, 张三, 34),new User(2, 李四, 23),new User(3, 王五, 34)
};
// 获取 name 中包含 张 的元素集合
var list userArr.MyWhere(p p.Name.Contains(张));
foreach (var user in list)Console.WriteLine(user);
/*Id: 1, Name: 张三, Age: 34*/ test 委托的组合 委托是可以使用“”号来进行组合的组合后会生成一个新的委托对象调用这个新的委托对象时会按顺序将组合进来的委托依次执行。看如下示例 using System;
using NUnit.Framework;namespace 委托的组合
{[TestFixture]public class Tests{[Test]public void Test1(){Actionstring a1 SayHello1;Actionstring a2 SayHello2;Actionstring a3 SayHello3;// 组合Actionstring a4 a1 a2 a3;a4(张三);/*hello 张三 from SayHello1hello 张三 from SayHello2hello 张三 from SayHello3*/}public void SayHello1(string name){Console.WriteLine(hello name from SayHello1);}public void SayHello2(string name){Console.WriteLine(hello name from SayHello2);}public void SayHello3(string name){Console.WriteLine(hello name from SayHello3);}}
} 委托的组合.Tests 还可以通过“-”号从委托对象中将已组合进来的委托对象移除如下 using System;
using NUnit.Framework;namespace 委托的组合
{[TestFixture]public class Tests{[Test]public void Test1(){Actionstring a1 SayHello1;Actionstring a2 SayHello2;Actionstring a3 SayHello3;// 组合Actionstring a4 a1 a2 a3;// 移除 a2 委托对象a4 a4 - a2;a4(张三);/*hello 张三 from SayHello1hello 张三 from SayHello3*/}public void SayHello1(string name){Console.WriteLine(hello name from SayHello1);}public void SayHello2(string name){Console.WriteLine(hello name from SayHello2);}public void SayHello3(string name){Console.WriteLine(hello name from SayHello3);}}
} 委托的组合.Tests 委托如果有返回值则有一些特殊不过委托的组合一般是给事件使用普通情况很少使用这里就不再深究。 事件 定义 给委托添加上 event 关键字它就是一个事件格式如下 [访问修饰符] event 委托类型 事件名称; 案例三本命年事件 1、修改 User 实体类定义一个事件让其在本命年时触发 using System;namespace MyTests.Entities
{public class User{public User(){}public User(int id, string name, int age){this.id id;this.name name;this.age age;}private int id;private string name;private int age;public int Id{get { return id; }set { id value; }}public string Name{get { return name; }set { name value; }}public int Age{get { return age; }set{if (age % 12 0) OnBirthYear(this.name);age value;}}public override string ToString(){return string.Format(Id: {0}, Name: {1}, Age: {2}, id, name, age);}public event Actionstring OnBirthYear; // 定义一个本命年触发的事件}
} MyTests.Entities.User 2、使用 var user1 new User();
var user2 new User();
var user3 new User();
// 给每一个 User 对象通过 注册事件
user1.OnBirthYear (name, age) { Console.WriteLine(name age 岁了本命年到了,喝稀饭); };
user2.OnBirthYear (name, age) { Console.WriteLine(name age 岁了本命年到了,吃馒头); };
user3.OnBirthYear (name, age) { Console.WriteLine(name age 岁了本命年到了,大鱼大肉); };user1.Id 1;
user1.Name 张三;
user1.Age 23; user2.Id 2;
user2.Name 李四;
user2.Age 24;user3.Id 3;
user3.Name 王五;
user3.Age 36;
/*李四24岁了本命年到了,吃馒头
王五36岁了本命年到了,大鱼大肉*/ 注册事件触发的方法时方法的参数及返回值要与事件的委托一致。 事件原理 事件的注册和移除实际上是通过事件的 add 和 remove 方法完成在这两个方法中维护了同一个委托对象且事件不可为 null。上述实体类也可修改如下 using System;namespace MyTests.Entities
{public class User{public User(){}public User(int id, string name, int age){this.id id;this.name name;this.age age;}private int id;private string name;private int age;public int Id{get { return id; }set { id value; }}public string Name{get { return name; }set { name value; }}public int Age{get { return age; }set{age value;if (age % 12 0){if (_OnBirthYear ! null){_OnBirthYear(this.name,this.age);}}}}public override string ToString(){return string.Format(Id: {0}, Name: {1}, Age: {2}, id, name, age);}private Actionstring, int _OnBirthYear; // 定义一个本命年触发的事件public event Actionstring, int OnBirthYear{add { _OnBirthYear value; }remove { _OnBirthYear - value; }}}
} MyTests.Entities.User 委托与事件总结 委托的作用 占位在不知道将来要执行方法的具体逻辑时可以先用一个委托变量来代替方法调用委托的返回值参数列表要确定。在实际调用之前需要为委托赋值。 事件的作用 事件的作用域委托变量一样只是在功能上相对委托变量有更多的限制比如 只能通过 或 - 来绑定方法事件处理程序。只能在类的内部调用触发事件。事件和委托的关系 反编译会发现事件是由一个私有的委托变量、 add_* 和 remove_* 方法组成而事件的非简化写法就是声明一个私有的委托变量和 add 、 remove 方法。 相关面试题 1、说一下事件和委托的关系 网上有很多答案说事件就是委托这个肯定是错误的。只能说事件的实现依赖于委托因为事件是由一个私有的委托变量、 add_* 和 remove_* 方法组成。 2、接口中可以定义事件吗那索引器和属性呢 首先接口中只可以定义方法的签名事件、索引器、属性本质上都是方法所以是可以定义的。 看如下示例 using System;
using System.Collections.Generic;namespace MyTests
{public interface Interface1{// 属性Listint list { get; set; }// 索引器long this[int index] { get; set; }// 事件event Actionstring, int OnEvent;}
} MyTests.Interface1 对应反编译文件为 .class public interface abstract auto ansi Interface1
{.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) { string(Item) }.event [mscorlib]System.Action2string, int32 OnEvent{.addon instance void MyTests.Interface1::add_OnEvent(class [mscorlib]System.Action2string, int32).removeon instance void MyTests.Interface1::remove_OnEvent(class [mscorlib]System.Action2string, int32)}.property instance int64 Item{.get instance int64 MyTests.Interface1::get_Item(int32).set instance void MyTests.Interface1::set_Item(int32, int64)}.property instance class [mscorlib]System.Collections.Generic.List1int32 list{.get instance class [mscorlib]System.Collections.Generic.List1int32 MyTests.Interface1::get_list().set instance void MyTests.Interface1::set_list(class [mscorlib]System.Collections.Generic.List1int32)}} MyTests.Interface1 反编译 IL 内容 可以看到接口中定义事件实际上是声明了 add_* 和 remove_* 方法的签名定义索引器实际上是声明了 set_Item 和 get_item 方法的签名而定义属性实际上是声明了 set_* 和 get_* 方法的签名。 转载于:https://www.cnblogs.com/zze46/p/10702364.html