函数式编程
一、函数式编程和面向对象编程
1、面向对象编程:利用继承封装多态来进行的编程
2、函数式编程的基础:避免状态突变和将函数作为第一个类的概念这两种方法
1>避免状态突变:定义不可变的类型或者不可变的集合,当类型不可变时,就无法改变他的实例,就可以使用多个线程并发访问而不需要同步
2>函数作为第一个类:意味着函数可以作为函数的参数,函数可以从函数中返回,函数可以赋予变量,这里涉及到两个概念高阶函数和纯函数
<1>高阶函数:在一个函数中将另一个函数作为参数,或者返回一个函数,或者返回两个函数
举例:
//T2作为T1委托函数的返回值传入f2中,返回值的跟f2保持一致
public static Func<T1, TResult> Compose<T1, T2, TResult>(Func<T1, T2> f1, Func<T2, TResult> f2) =>(a => f2(f1(a)));
//匿名委托,调用
Func<int, int> f1 = x => x + 1;
Func<int, int> f2 = x => x + 2;
Func<int, int> f3 = Compose(f1, f2);
var x1 = f3(39);
WriteLine(x1);
var greetPerson = Compose(new Func<string, Person>(name => new Person(name)), person => $"Hello, {person.FirstName}");
WriteLine(greetPerson("Mario Andretti"));
<2>纯函数:满足两个条件 1>始终给传递的相同参数返回相同的结果;2>不产生副作用,如改变状态或者依赖外部资源
二、表达式体的成员
1、当方法或者属性只有一行实现代码时,为了减少代码的实现
常规语法:member => expression;
三、扩展方法
1、扩张方法定义在静态类中定义,定义为一盒静态方法,其中第一个参数指定要扩展的类型,用this指定
2、将方法写入最初,没有提供此方法的类中,还可以将书方法添加到特定接口的任何类中,这样多个类就可以使用相同的实现代码
举例一:
public static IEnumerable Where(this IEnumerable source,Fun<TSource,bool> predicate)
{
foreach(TSource item in source)
{
if(predicate(item))
{
yiled return item;
}
}
}
举例二:
//using语句改写为下列方法
public static void Use(this T item, Action action) where T : IDisposable
{
using (item)
{
action(item);
}
}
三、本地函数
1、什么是本地函数:在方法中申明方法,本地函数在方法的作用域,属性访问器,构造函数或者lambda表达式体内声明
2、本地函数有时候跟委托对比:相比于lambda表达式本地函数的语法更简单,相比于委托,执行更加出色,委托需要类的一个实例和一个引用的集合,而本地的函数只需要对函数的引用就可以,节省开销
3、本地函数不能用abstract、private、virtual 修饰,允许用async和unsafe修饰
4、本地函数和yield语句
举例说明,重写where方法,正常的时候会延迟查询,只有foreach读值的时候才会报错,当使用本地函数时,会在查询时就提前报错:
public static IEnumerable Where(this IEnumerable source, Func<T, bool> predicate)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
return Iterator(); IEnumerable<T> Iterator() { foreach (T item in source) { if (predicate(item)) { yield return item; } } } }