首页 > 其他分享 >.NET学习系列之委托

.NET学习系列之委托

时间:2023-02-05 03:22:10浏览次数:60  
标签:系列 委托 int void values static NET public

委托(delegate)

委托是一种知道如何调用方法的对象。
委托类型定义了一种委托实例(delegate instance)可以调用的方法。具体来说,它定义了方法的返回类型(return type)和参数类型(parameter type)。
举例说明:

internal class Program
 {
     //定义委托
     private delegate int Transformer(int x);
     //定义方法
     private static int Square(int x) => x *= x;

     private static void Main(string[] args)
     {
         //Transformer t = Square === Transformer t=new Transformer(Square);
         Transformer t = Square;
         //t(3)===t.Invoke(3);
         int result = t(3);
         Console.WriteLine(result);
     }
 }

委托和回调(callback)类似。一般指捕获类似C函数指针的结构。

用委托写插件方法

.NET 中的委托插件方法是指通过使用委托的方式,在NET 程序中动态地注入外部代码,从而实现插件的功能。委托是.NET 程序设计中的一个重要概念,可以实现方法的封装和传递。通过委托,程序可以动态地注册外部代码,从而实现插件的功能。

//定义委托
 public delegate int Transformer(int x);

 public class Program
 {
     //定义方法
     private static int Square(int x) => x *= x;

     private static void Main(string[] args)
     {
         int[] values = { 1, 2, 3 };
         Util.Transform(values, Square);
         foreach (var item in values)
         {
             Console.WriteLine(item + "  ");
         }
     }
 }

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

多播委托

所有的委托实例都拥有多播能力。这意味着一个委托实例可以引用一个目标方法,也可以引用一组目标方法。委托可以使用+和+=运算符联结多个委托实例。
ps:相当于一个委托可以执行多个方法。

public delegate void ProgressReporter(int percentComplete);

  public class Program
  {
      //定义方法
      private static void Main(string[] args)
      {
          ProgressReporter p = WriteProgressToConsole;
          p += WriteProgressToFlie;
          Util.HardWork(p);
      }

      private static void WriteProgressToConsole(int precentComplete) => Console.WriteLine(precentComplete);

      private static void WriteProgressToFlie(int percentComplete) => System.IO.File.WriteAllText("progress.txt", percentComplete.ToString());
  }

  public class Util
  {
      public static void HardWork(ProgressReporter p)
      {
          for (int i = 0; i < 10; i++)
          {
              p(i * 10);
              System.Threading.Thread.Sleep(100);
          }
      }
  }

委托是不可变的,因此调用+=和-=的实质是创建一个新的委托实例,并把它赋值给已有的变量。
如果一个多播委托拥有非void的返回类型,则调用者将从最后一个触发的方法接收返回值。前面的方法依然调用,但是返回值都会被丢弃。大部分调用多播委托的情况都会返回void类型,因此这个细小的差异就没有了。

实例目标方法和静态目标方法

将一个实例方法赋值给委托对象时,后者不但要维护方法的引用,还需要维护方法所属的实例的引用。System.Delegate类Target属性代表这个实例(如果委托引用的是一个静态方法,则该属性值为null,所以将静态方法赋值给委托时性能更优。)

class Program {
    static void Main(string[] args) {
        X x = new X();
        ProgressReporter p = x.InstanceProgress;
        p(1);
        Console.WriteLine(p.Target == x); // True
        Console.WriteLine(p.Method); // Void InstanceProgress(Int32)      
        }
}
 class X {
     public void InstanceProgress(int percentComplete) {
        // do something    
         }
}

泛型委托类型

可以包含泛型类型参数。

//定义委托
    public delegate T Transformer<T>(T arg);

    public class Program
    {
        //定义方法
        private static int Square(int x) => x *= x;

        private static void Main(string[] args)
        {
            int[] values = { 1, 2, 3 };
            Util.Transformer(values, Square);
            foreach (var item in values)
            {
                Console.WriteLine(item + "  ");
            }
        }
    }

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

Func和Action委托

具有任意的返回类型和任意数目的参数。
辅助理解博客:Func和Action委托详解
1:Action用于没有返回值的方法(参数可以根据自己情况进行传递)
2:Func恰恰相反用于有返回值的方法(同样参数根据自己情况情况)
3:记住无返回就用action,有返回就用Func
委托和接口
能用委托解决的问题,都可以用接口解决。

public class Program
 {
     private static void Main(string[] args)
     {
         int[] values = { 1, 2, 3 };
         Util.TransforAll(values, new Squarer());
         foreach (var item in values)
         {
             Console.WriteLine(item + "  ");
         }
     }
 }

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

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

 internal class Cuber : ITransformer
 {
     public int Transform(int x) => x * x * x;
 }

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

如果以下一个或多个条件成立,委托可能是比接口更好的选择:

  • 接口内仅定义了一个方法
  • 需要多播能力
  • 订阅者需要多次实现接口

委托的兼容性

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

  • 如果委托实例指向相同的目标方法,则认为它们是等价的
    image

如果多播委托按照相同的顺序引用相同的方法,则认为他们是等价的。

逆变和协变

协变逆变正是利用继承关系 对不同参数类型或返回值类型 的委托或者泛型接口之间做转变。
如果一个方法要接受Dog参数,那么另一个接受Animal参数的方法肯定也可以接受这个方法的参数,这是Animal向Dog方向的转变是逆变。如果一个方法要求的返回值是Animal,那么返回Dog的方法肯定是可以满足其返回值要求的,这是Dog向Animal方向的转变是协变。
由子类向父类方向转变是协变 协变用于返回值类型用out关键字
由父类向子类方向转变是逆变 逆变用于方法的参数类型用in关键字
协变逆变中的协逆是相对于继承关系的继承链方向而言的。
辅助理解博客:逆变和协变详解

标签:系列,委托,int,void,values,static,NET,public
From: https://www.cnblogs.com/FaceMan/p/17092785.html

相关文章