首页 > 其他分享 >第四章 高级特性——委托、事件、匿名方法和Lambda表达式

第四章 高级特性——委托、事件、匿名方法和Lambda表达式

时间:2022-11-13 20:12:06浏览次数:57  
标签:Console 委托 int void 匿名 static delegate 表达式 Lambda

4.1 委托

什么是委托?

委托是一种知道调用方法的对象。

delegate int Dele(int x);

class Test{
    static int Square(int x)=>x*x;    

    static void Main(){
        Dele d=Square;
        int result=d(4);// 16
    }
}

多播委托

所有的委托实例都拥有多播能力。即一个委托实例可以引用一组目标方法。

  • 使用 += -=
delegate void Dele(int x);

class Test{
    static void Square(int x)=>Console.Write(x*x);    
  static void Cube(int x)=>Console.Write(x*x*x);    

    static void Main(){
        Dele d=Square;
        d+=Cube;
        d(4);// 16  64
    }
}

如果委托的方法组有返回值,则委托实例返回最后一个委托方法的返回值。

泛型委托

委托类型可以包含泛型委托类型参数。

public delegate T Dele<T>(T arg);

public class Util{
    public static void Transform<T>(T[] values,Dele<T> d){
        for(int i=0;i<values.Length;i++){
            values[i]=d(valuse[i]);
        }
    } 
} 

public class Test{
    static int Square(int x)=>x*x;

    static void Main(){
        int[] ints={1,2,3};
        Util.Transform(ints,Square);

        foreach(int i in ints){
            Console.Write(i+" ");// 1 4 9
        }
    }
}

Func和Action委托

通用的小型委托类型,具有任意返回类型和(合理的)任意数目的参数。

 

对比上例:

public class Util{
    public static void Transform<T>(T[] values,Func<T,T> d){
        for(int i=0;i<values.Length;i++){
            values[i]=d(valuse[i]);
        }
    } 
} 

public class Test{
    static int Square(int x)=>x*x;

    static void Main(){
        int[] ints={1,2,3};
        Util.Transform(ints,Square);

        foreach(int i in ints){
            Console.Write(i+" ");// 1 4 9
        }
    }
}

委托与接口

能用委托解决的问题,都可以用接口解决。

对比上例:

public interface ITransformer{
    int Transformer(int x);
}

public class Util{
    public static void TransformAll(int[] values,ITransformer t){
        for(int i=0;i<values.Length;i++){
            values[i]=t.Transformer(valuse[i]);
        }
    } 
} 

public class Squarer:ITransformer{
    public int Transformer(int x)=>x*x;    
}

public class Test{
    static void Main(){
        int[] ints={1,2,3};
        Util.TransformAll(ints,new Squarer());
        foreach(int i in ints){
            Console.Write(i+" ");// 1 4 9
        }
    }
}

优先使用委托的情况:

(1)接口内仅定义了一个方法

(2)需要多播能力

(3)订阅者需要多次实现接口

委托的兼容性

即使签名相似,委托类型也互不兼容。

delegate void D1();
delegate void D2();

D1 d1=Method;
D2 d2=d1 // 编译错误
D2 d2=new D2(d1);

如果委托实例指向同一个方法,则认为它们是等价的。

delegate void D();

D d1=Method();
D d2=Method();
Console.WriteLine(d1==d2); // true

委托可以比指向的目标方法的参数更具体化,这称为逆变

delegate void StringAction(string s);

class Test{
    static void Method(object o)=>Console.WriteLine(o);

    static void Main(){
        StringAction sa=new StringAction(Method);
        sa("hello"); // hello
    }
}

// 这里object->string->object,后部分向上转型

指向的目标方法的参数可以比委托更具体化,这称为协变

delegate object StringAction();

class Test{
    static string Method()=>Console.WriteLine("hello");

    static void Main(){
        StringAction sa=new StringAction(Method);
        object o=sa;
        Console.WriteLine(o); // hello
    }
}

// 这里string->object->string,后部分向下转型

自定义泛型委托,最好使用下列标准(为了类型的关系自然的转化):

(1)将只用于返回值类型的类型参数标记为协变(out

(2)将只用于参数的任意类型参数标记为逆变(in

delegate TResult Func<out TResult>();
Func<string> x=...;
Func<object> y=x;// string->object

delegate void Action<in T>(T arg);
Func<object> x=...;
Func<string> y=x;// object->string

4.2 事件

什么是事件?

public delegate void TestHandler();

public class Test{
    public event TestHandler TestChanged;
} 

事件修饰符:virtual、override、abstract、sealed

事件有什么用?

上例中,TestChanged在Test类内当作一个委托使用。

而Test类外面的TestHandler委托,只能在TestChanged事件身上进行+=、-=操作。

这样做的目的就是为了保证订阅者之间不互相影响。提高健壮性

4.3 匿名方法

什么是匿名方法?

没有方法名的方法,只能用一次,必须是一个语句块 { }

delegate int Dele(int i);

Dele d=delegate (int x) {return x*x;};
Console.WriteLine(d(4)); // 16

4.4 Lambda表达式

什么是Lambda表达式?

Lambda表达式是一种可以替代委托实例的匿名方法。

对比上例:

delegate int Dele(int i);

Dele d1= (int x)=> {return x*x};
Console.WriteLine(d1(4)); // 16

Dele d2= x=> x*x;
Console.WriteLine(d2(4)); // 16

形式:

(参数) => 表达式或者语句块

  • 为了方便,只有一个可推测类型的参数时,可以省略小括号。

Lambda表达式通常和Func和Action委托一起使用。对比上例:

Func<int,int> d= x=>x*x;

显示指定Lambda参数的类型

编译器可以根据上下文推断出Lambda表达式的类型,但是当无法判断时就必须显示指定每一个参数的类型。

void Foo<T>(T x){}
void Bar<T>(Action<T> x){}

//Bar(x=>Foo(x)); // ??无法判断
Bar((int x)=>Foo(x));
Bar<int>( x=>Foo(x) );
Bar<int>(Foo);

捕获外部变量

Lambda表达式可以引用方法内定义的局部变量和方法的参数。

  • 这些变量和外部参数称为捕获变量,捕获变量的表达式称为闭包

捕获变量在真正调用委托时赋值,而不是在捕获时赋值。

Lambda表达式可以更新捕获的变量的值。

捕获变量的生命周期和委托的生命周期一样,委托执行完毕后,捕获变量会从作用域消失,变量值会变回去。

Lambda表达式和局部方法的对比

局部方法和Lambda表达式的相应功能是重叠的。

局部方法的优势:

(1)局部方法可直接调用自己(递归)

(2)局部方法避免了定义杂乱的委托类型

(3)局部方法的开销更小

 

标签:Console,委托,int,void,匿名,static,delegate,表达式,Lambda
From: https://www.cnblogs.com/swbna/p/16886768.html

相关文章

  • Python正则表达式入门
    Python正则表达式入门文章目录​​Python正则表达式入门​​​​1、在Python中使用正则表达式​​​​2、最基础正则表达式​​​​3、正则匹配函数​​​​正则表达式的字......
  • re-正则-表达式规则
    re正则表达式模式   ......
  • 从阿里云函数迁移到 aws lambda
    阿里云函数计算最近开始取消每个月的免费额度,吃相难看。虽然我平时跑跑个人的定时任务,用到的资源很少,还是决定迁移到别的平台。让它日活-1也算我做的一个贡献吧。1/安装......
  • 学习笔记-java代码审计-表达式注入
    java代码审计-表达式注入0x01漏洞挖掘spelspel表达式有三种用法:注解@value("#{表达式}")publicStringarg;这种一般是写死在代码中的,不是关注的重点。xml<b......
  • JSP—El表达式,java脚本,java表达式,jstl标签库
    jsp简介JSP(全称JavaServerPages)是由SunMicrosystems公司倡导和许多公司参与共同创建的一种使软件开发者可以响应客户端请求,而动态生成HTML、XML或其他格式文档的We......
  • 中缀表达式转后缀
    例:(6+3*(7-4))-8/2首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为存放结果(逆波兰式)的栈S2(空栈),S1栈可先放入优先级最低的运算符#,注意,中缀式应以此......
  • Java Lambda 表达式
    (目录)一、说明Lambda表达式是什么Lambda表达式也称为闭包,是Java8发布的新特性Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)为什么要使用Lamb......
  • Jsp中EL表达式不起作用
    困扰了很久的JSP中的表达式被当成字符串处理,原因是出于版本不同,有些isELIgnored属性默认值为false,但有些默认为true;所以这里要手动配置isELIgnored的属性为false<%@pag......
  • Python语法糖之赋值表达式
    目录概述语法(海象运算符)为什么需要这个?避免重复写代码赋值表达式写法例子:连续输入命令行避免重复计算赋值表达式写法在推导式中应用例子:字典倒查本博客主要参考为北京大学......
  • 【正则】964- 正则表达式的括号如何使用?
    最近再一次重温老姚大佬的《JavaScript正则表达式迷你书》,并将核心知识点整理一遍,方便复习。原书写得非常棒,建议看下原书啦。  地址:https://github.com/qdlaoyao/js-re......