C#算是个多范式编程语言, 除了传统的OO风格, 还可以在部分范围中使用函数式编程的风格, 这里整理一下C#中委托和Lambda实践中的各种写法.
这里不会解释具体的代码含义, 仅仅介绍写法, 可能不适合刚开始学习.
声明委托类型
使用前必须要有具体的委托类型, 下面的例子中会使用到这些常用的委托类型
1 2 3 4 5 6 7 |
|
Action
和Action<in T1, in T2>
是在dotnet 3.5 sp1出现的一个很实用的委托, 类似的还有1-16个参数的, 这里主要使用这2种.
Predicate
是从dotnet 2.0就出现的, 一般是在泛型集合的查询中使用.
Func<in T1, in T2, out TResult>
也是dotnet 3.5 sp1出现的, 和Action
基本一样, 也有1-16个参数的, 和Action不同的是这个委托都声明有返回值类型, 而不是Action
的void.
最初的写法
最简单的Action
1 2 3 4 5 6 7 8 9 |
|
对于复杂的委托, 比如Func
1 2 3 4 5 6 7 8 9 |
|
匿名委托
和上一个区别就是不需要创建Foo
方法了
1 2 3 4 |
|
Predicate
就是这样的
1 2 3 4 |
|
右边的new xxxx()
可以省略
1 2 3 4 |
|
由于也是一行代码, 结尾的分号还是必须的.
也适用于最初的写法
1 2 3 4 5 6 7 8 9 |
|
如果使用的是dotnet 4的编译器, 可以使用var
, 看起来就像是颠倒过来了
1 2 3 4 |
|
var
是编译器提供的魔法, 会自动推导=
右边的类型, 当然前提是右边的可以推导出来类型, 无法推导出来就会编译错误.
Lambda登场
1 2 3 4 5 6 7 |
|
语法方面, 无参数要写成()
, 1个参数可以省略括号, 2个及更多参数则必须括号()
; =>
右边必须有表达式; 表达式结果必须是委托的返回值类型, 如果委托返回值类型是void则无所谓表达式结果类型.
Lambda也算是编译器魔法, 上述Lambda表达式特点都是左边的委托类型明确, 即委托的参数, 返回值也是明确的, 和var
相似, 类型是可推导出来的, 那么就可以使用Lambda, 这样就省了写一堆的参数类型和return语句.
实际使用中可能是这样的
1 2 3 4 |
|
即除了声明变量, 参数是明确的委托类型时, 也可以使用Lambda.
Lambda的=>
右边也可以是多行代码
1 2 3 4 5 6 7 8 9 |
|
委托可以叠加
1 2 3 4 5 6 7 |
|
执行时会按照先加先执行的顺序, 如果有返回值, 那么将使用最后加进来的委托的返回值.
同样也可以从叠加的里面减去, 不过这里是按引用, 所以需要保留一个加进去的委托引用.
1 2 3 4 5 6 |
|
事件
C#提供了一个event
关键字用于声明一种特殊的委托, 可以在类外部+=
-=
, 但是执行委托只能在内部
1 2 3 4 5 6 7 8 9 10 11 |
|
默认的事件实现中+=
-=
是线程安全的, 这点可以在反编译event
的源码中看到, 如果要自行实现event
的+=
-=
则需要自行处理线程安全.
总结
使用委托可以避免创建太多的中间方法, 而使用Lambda, 则可以使在写委托的时候避免大量的delegate
关键字和重复的委托参数类型声明.
也许会让代码不是很容易理解, 但是只要遵循一些约定, 熟悉了还是没关系的.
重要的是这会少写很多重复的东西, 同样修改时也少修改一些东西.