C#学习笔记(1)-Delegates、Lambdas和Even

2021-10-04  本文已影响0人  后青春期的诗大喵

1.Delegates

①首先Delegate无法在Objective-C找到对应概念。
②Delegate首先是一个类,可以包含方法的地址,并且可包含多个方法。Delegate对方法进行了一层包装,从而可以实现C#的语言特性。C和C++中是方法地址,但并不安全,Delegate包装一层,所以更安全。
③Lambda表达式由delegate实现,Evnets也需要由delegate实现。
④Delegate比如可以用于多线程,用于泛型库的编程,用于Event的编程。
⑤Delegate继承自 System.MuticastDelegate,继承自 System.Delegate

1)Delegate的声明
public delegate void IntMethodInvoker(int x);

可以理解为delegate是一个Class类似的概念,delegate后面的方法和参数和返回值,表明了delegate保存的是什么类型的方法的地址。

2)Delegate的使用
private delegate string GetAString();
int x = 40;
GetAString firstStringMethod = new GetAString(x.ToString);
Console.WriteLine($"String is {firstStringMethod()}");

先定义delegate,然后new一个delegate对象,传入对象x的方法ToString(方法作为delegate对象的构造参数),最后调用实例化的delegate对象的执行方法,(),可以使用invoke调用执行。

其中

GetAString firstStringMethod = new GetAString(x.ToString);

可以缩写为

GetAString firstStringMethod = x.ToString;

理解上仍然需要知道是new了一个delegate对象。

3) Action <T> 和 Func <T> Delegates

Action用于定义没有返回值的Delegate,参数可以有多个

Func 用于定义有返回值的Delegate,参数可以有多个

主要目的:减少自己去定义Delegate。

比如定义一个比较大小的Delegate,用Func<T, T, bool>。

class BubbleSorter
    {
        static public void Sort<T>(IList<T> sortArray, Func<T, T, bool> comparison)
        {
            bool swapped = true;
            do
            {
                swapped = false;
                for (int i = 0; i < sortArray.Count -1; i++)
                {
                    if (comparison(sortArray[i+1] ,sortArray[i]))
                    {
                        T temp = sortArray[i];
                        sortArray[i] = sortArray[i + 1];
                        sortArray[i + 1] = temp;
                        swapped = true;
                    }
                }
            } while (swapped);
        }
    }

在Employee中实现Delegate定义的比较大小的方法

public static bool CompareSalary(Employee e1, Employee e2) =>
e1.Salary < e2.Salary;

然后调用时传入对应的Delegate方法的定义的实现即可。

BubbleSorter.Sort(employees, Employee.CompareSalary);
4) 多播Delegates

一般用于保存于Delegate中的方法依次执行,不保证有序,一般需要都是无返回值的。有返回值,只会返回最后一个执行的方法的返回值,意义不大。注意:如果一个方法报错,后续的方法都不会执行了。如果仍然想要报错后面的方法执行,则需要自己手写遍历调用。

Action<double> operations = MathOperations.MultiplyByTwo;
perations += MathOperations.Square;
perations -= MathOperations.Square;
5)匿名方法

定义:是一个代码块,可以传给delegate(用delegate包装)。用于方法只使用一次的情况。多次还是要自己去定义方法。很少用,因为有lambda表达式。

string mid = ", middle part,";
Func<string, string> anonDel = delegate(string param) {
    param += mid;
    param += " and this was added to the string."; 
  return param;
};
Console.WriteLine(anonDel("Start of string"));

思考:delegate本质是包装了方法后的对象。并且可以存储和传递。在objective-c中找不到类似的概念。

2.Lambdas

1) Lambda表达式

匿名方法可以写为LAMBDA表达式,Lambda表达式本质是一个方法包装为了一个delegae。

string mid = ", middle part,"; 
Func<string, string> lambda = param => {
    param += mid;
    param += " and this was added to the string."; 
  return param;
};
Console.WriteLine(lambda("Start of string"));
2) Closure 闭包

定义:lambda表达式使用了其外部的变量。

闭包中引用的外部变量,在闭包执行时捕获其值。因为执行时才产生匿名类,此类copy外部的变量。类似于objective-C的block捕获,block本质也是一个类。原文如下:

Using the lambda expression and invoking the method creates an instance of the anonymous class and passes the value of the variable from the time when the call is made.

思考:C#的闭包还是和objective-c的闭包有很大差别,object-c的闭包可以定义__block来标记是否可以更改block外部的对象,C#没有这么细致的拆分。

3.Events

定义:Event通过delegate实现,提供订阅和发布机制。用处,比如button的点击事件。Events需要传递,第一个参数是发送者事件者本身,第二个是其他信息。此传递被包装为需继承EventArgs的类。

定义Event

public event EventHandler<CarInfoEventArgs> NewCarInfo;

等价于定义特殊的delegate

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e) where TEventArgs: EventArgs

等价于

private EventHandler<CarInfoEventArgs> _newCarInfo; 
public event EventHandler<CarInfoEventArgs> NewCarInfo {
    add => _newCarInfo += value;
    remove => _newCarInfo -= value; 
}

完整的例子如下

1) 定义传递的消息包装类,继承自EventArgs
① 定义消息
public class CarInfoEventArgs:EventArgs  
{
  public CarInfoEventArgs(string car) => Car = car;
    public string Car { get; }
}
2) 定义消息发布者,注意发布者的Invoke传递自己和消息。
public class CarDealer
{
  ② 用来保存方法
    public event EventHandler<CarInfoEventArgs> NewCarInfo; 

    public void NewCar(string car)
    {
        Console.WriteLine($"CarDealer,new car{car}");
        NewCarInfo?.Invoke(this,new CarInfoEventArgs(car));
    }
}
3) 定义消息订阅者,注意订阅的方法和发布的方法的结构一致,包括sender和消息
public class Consumer
{
    private string _name;
    public Consumer(string name) => _name = name;

    ③ 定义收到通知的处理方法
    public void NewCarIsHear(Object sender, CarInfoEventArgs e) 
    {
        Console.WriteLine($"{_name}: car{e.Car} is new");
    }
}
4) 建立发布者和订阅者的关联,注意订阅者的执行方法保存在发布者的delegate对象中。
public static void Main(string[] args)
{
    var dealer = new CarDealer();// 发布者
    var valtteri = new Consumer("Valtteri"); // 订阅者
  
  ④ 保存方法的delegate和收到通知的方法建立联系
    dealer.NewCarInfo += valtteri.NewCarIsHear; // 建立关联
    ⑤ 发送通知
  dealer.NewCar("Williams"); // 发布者发布

}

思考:发布订阅模式广泛应用于c#的各类应用中。建立发布订阅的流程项目objective-c较繁琐。objective-c只需要两个方法即可建立对象间的订阅和发布关系,并且不用特别定义传递的消息,消息可以直接是自定义的类。相对来说C#使用这种模型,就比较繁琐,需要标注的①②③④⑤四步,要写的代码也较多。这也不难理解Windowsphone为什么开发效率不高。

本文是对《Professional C# 7 and NET Core 2.0》第八章的读书笔记。

上一篇下一篇

猜你喜欢

热点阅读