C# 委托、lambda表达式和事件 (7) 持续更新

时间:2021-10-10 19:25:18

引用方法

在C++,函数指针只不过是一个指向内存位置的指针,它不是类型安全的。

C# 委托 定义了返回类型和参数的类型。委托类包含对方法的引用,还可以包含多个方法引用。

定义委托

 public delegate double TwoLongsOp(long first, long second);

 public delegate string GetAString();

委托派生自 System.MulticastDelegate,而 System.MulticastDelegate 又派生自 System.Delegate。

public delegate string GetAString();

static void Main(string[] args)
{
    int x = 10;
    Console.WriteLine(x.ToString());

    GetAString stringFun = new GetAString(x.ToString);
    Console.WriteLine(stringFun());
}

// 以下两种方法一样
stringFun();
stringFun.Invoke();

 

 GetAString stringFun = new GetAString(x.ToString);
 GetAString stringFun2 = x.ToString;

委托它们类型是安全的,可以确保被调用的方法的签名是正确的。但它们不关心什么类型的对象上调用该方法,甚至不考虑该方法是静态方法,还是实例方法。

class MyClass
{
    public static string getStr()
    {
        return "Hello";
    }
}

static void Main(string[] args)
{
    GetAString stringFun = MyClass.getStr;
    Console.WriteLine(stringFun());
}

委托数组

 GetAString[] stringFun = { MyClass.getStr };
 Console.WriteLine(stringFun[0]());

 

泛型 Action<T> 委托表示引用一个 void 返回类型的方法。Action 可以调用 0 ~ 16 个参数的方法。

Func<T> 引用 带 返回类型的方法,Func可以调用 0 ~ 16 个参数的方法。

class MyClass
{
    public static string getString(string s)
    {
        return "Hello "+ s;
    }

    public static void Call()
    {
        Console.WriteLine("Hello Call");
    }

}

static void Main(string[] args)
{
    Func<string, string> stringFun = MyClass.getString;
    Console.WriteLine(stringFun("Wo"));
    Action fun2 = MyClass.Call;
    fun2();
}

 

多播委托

存储两个方法的引用

增加 +=   删除 -=  

class MyClass
{
    public static void Hello(string s)
    {
        Console.WriteLine("Hello " + s);
    }

    public static void Call(string s)
    {
        Console.WriteLine("Call " + s);
    }

}

static void Main(string[] args)
{
    Action<string> action = MyClass.Hello;
    action += MyClass.Call;

    action("Zhao");

    action -= MyClass.Call;

    action("Zhao2");
}

如果 Hello 方法报错了,第二个 Call 就不会执行了。可以用以下代替。

Action<string> action = MyClass.Hello;
action += MyClass.Call;

Delegate[] delegates = action.GetInvocationList();
foreach (Action<string> action2 in delegates)
{
    try
    {
        action2("Python");
    }
    catch (Exception)
    {
    }
}

 

匿名方法

Action<string> action = delegate(string str)
{
    Console.WriteLine("Anonymous " + str);
};

Delegate[] delegates = action.GetInvocationList();
foreach (Action<string> action2 in delegates)
{
    try
    {
        action2("Python");
    }
    catch (Exception)
    {
    }
}

匿名方法不能使用跳转语句(break、goto 或 continue)跳到该匿名方法的外部。匿名方法内部能访问不安全的代码。也不能使用 ref 和 out 参数。但可以使用匿名方法外部定义的其他变量。

 

lmadba

Action<string> action = str => Console.WriteLine("lambda " + str);
Action<string> action2 = (str) => { Console.WriteLine("lambda " + str); };

匿名方法

int sum = 12;
Func<int, int> fun = x => x + sum;
Console.WriteLine(fun(12) + "  " + sum);

 

事件 

class Program
{
    public class CustomEventArgs : EventArgs
    {
        public CustomEventArgs(string car)
        {
            this.Car = car;
        }

        public string Car { get; private set; }
    }


    public static event EventHandler<CustomEventArgs> NewEvent;

    static void Main(string[] args)
    {
        NewEvent += NewEventHandler;
        NewEvent += NewEventHandler;
        NewEvent -= NewEventHandler;
        CustomEventArgs eventArgs = new CustomEventArgs("Audio");
        NewEvent(null, eventArgs);
    }

    static void NewEventHandler(object sender, CustomEventArgs e)
    {
        Console.WriteLine("事件处理参数 " + e.Car);
    }
}

 

 NewEvent += NewEventHandler;        // 添加事件
 NewEvent -= NewEventHandler;        // 移除事件

 

弱事件

在不需要使用事件时,需要移除事件。否则的话,会出现内存泄露。另一种方法用弱事件。 

public class WeakCarInfoEventManager : WeakEventManager
{
  public static void AddListener(object source, IWeakEventListener listener)
  {
    CurrentManager.ProtectedAddListener(source, listener);
  }

  public static void RemoveListener(object source, IWeakEventListener listener)
  {
    CurrentManager.ProtectedRemoveListener(source, listener);
  }

  public static WeakCarInfoEventManager CurrentManager
  {
    get
    {
      WeakCarInfoEventManager manager = GetCurrentManager(typeof(WeakCarInfoEventManager)) as WeakCarInfoEventManager;
      if (manager == null)
      {
        manager = new WeakCarInfoEventManager();
        SetCurrentManager(typeof(WeakCarInfoEventManager), manager);
      }
      return manager;
    }
  }


  protected override void StartListening(object source)
  {
    (source as CarDealer).NewCarInfo += CarDealer_NewCarInfo;
  }

  void CarDealer_NewCarInfo(object sender, CarInfoEventArgs e)
  {
    DeliverEvent(sender, e);
  }
  protected override void StopListening(object source)
  {
    (source as CarDealer).NewCarInfo -= CarDealer_NewCarInfo;
  }
}

侦听

var dealer = new CarDealer();

var michael = new Consumer("Michael");
WeakCarInfoEventManager.AddListener(dealer, michael);

dealer.NewCar("Mercedes");

var sebastian = new Consumer("Sebastian");
WeakCarInfoEventManager.AddListener(dealer, sebastian);

dealer.NewCar("Ferrari");

WeakCarInfoEventManager.RemoveListener(dealer, michael);

dealer.NewCar("Red Bull Racing");

 

还可以用内置的 泛型弱事件 写的代码更少

 

var dealer = new CarDealer();

var michael = new Consumer("Michael");
WeakEventManager<CarDealer, CarInfoEventArgs>.AddHandler(dealer, "NewCarInfo", michael.NewCarIsHere);

dealer.NewCar("Mercedes");

var sebastian = new Consumer("Sebastian");
WeakEventManager<CarDealer, CarInfoEventArgs>.AddHandler(dealer, "NewCarInfo", sebastian.NewCarIsHere);

dealer.NewCar("Ferrari");

WeakEventManager<CarDealer, CarInfoEventArgs>.RemoveHandler(dealer, "NewCarInfo", michael.NewCarIsHere);

dealer.NewCar("Red Bull Racing");