zoukankan      html  css  js  c++  java
  • C#之委托

      如果要给方法传递一个方法参数时,就可以使用委托。要传递方法,就必须把方法的细节封装在一钟新类型的对象中,即委托。委托是一种特殊类型的对象,其特殊之处在于,我们以前定义的所有对象都包含数据,而委托只包含一个或多个方法的地址。
      .NET版本中,委托指向方法的地址。在C++中,函数指针是一个指向内存位置的指针,但它不是类型安全的。开发者无法判断这个指针实际指向什么,像参数和返回值等项就更不知道了。
      .NET委托是类型安全的类,它定义了返回类型和参数的类型。委托类不仅包含对方法的引用,也可以包含对多个方法的引用。
    1.声明委托
      使用委托和使用类一样,也需要经过定义和实例化两个步骤。首先必须定义要使用的委托,对于委托,定义它就是告诉编译器这种类型的委托表示哪种类型的方法。然后,必须创建该委托的一个或多个实例才能使用。编译器在后台将创建表示该委托的一个类。
      定义委托的语法:
      delegate void IntMethod(int x);//定义了一个委托IntMethod,指定该委托的每个实例都可以包含一个或多个方法的引用,引用的方法必须带有一个int参数,并返回void.

      因为定义委托基本上是定义一个新类,所以可以在定义类的任何地方定义委托。也可以在委托的定义上使用修饰符:public,private,protected等。
      委托派生自基类System.MulticastDelegate,MulticastDelegate又派生自基类System.Delegate.
      类有两个不同的术语:“类”表示广义的定义,“对象”表示;类的实例。但委托只有一个术语。在创建委托的实例时,所创建的实例仍称为委托。必须从上下文中确定委托的具体含义。

    2.使用委托
      定义好委托之后,就可以创建它的一个实例,从而用它存储特定方法的细节。  

                    delegate void IntMethod(int x);
    
            static void Fun(int x)
            {
                Console.WriteLine(x);
            }
    
            static void Main()
            {
              int x = 40;
              IntMethod intMethod = new IntMethod(Fun);
              intMethod(x);
              Console.ReadKey();
    
            }

      委托在语法上总是接受一个参数的构造函数,这个参数就是委托引用的方法。这个方法必须匹配最初定义委托时的签名。
      使用委托实例的名称,后面加上圆括号,如果需要参数就必须在圆括号内加上参数。
      给委托实例提供圆括号和调用委托类的Invoke()方法完全相同:
      intMethod(x);
      intMethod.Invoke(x);

      为了减少输入量,只需要给委托实例传递方法地址的名称就可以,这称为委托推断。
      IntMethod intMethod = new IntMethod(Fun);
      IntMethod intMethod =Fun;
      委托推断可以在需要委托实例的任何地方使用。委托推断也可以用于事件,因为事件基于委托。(事件后面文章有介绍)

      注意,使用委托可以调用任何类型对象的方法,不管是静态方法还是实例方法。

    3.使用委托数组

      

              //先在一个类中定义两个方法:
              class MathOperations
              {
                public static double MultiplyByTwo(double value)
                {
                  return value * 2;
                }
    
                public static double Square(double value)
                {
                  return value * value;
                }
              }
                //定义一个返回double类型且带有double类型参数的委托
              delegate double DoubleOp(double x);
    
              
              class Program
              {
                static void Main()
                {
                  //实例化委托数组,和实例化类的数组一样
                  DoubleOp[] operations =
                  {
                    MathOperations.MultiplyByTwo,
                    MathOperations.Square
                  };
                  
                  //遍历数组,使用数组中的每个委托实例
                  for (int i = 0; i < operations.Length; i++)
                  {
                    Console.WriteLine("Using operations[{0}]:", i);
                    //将委托实例作为参数传递给ProcessAndDisplayNumber方法
                    ProcessAndDisplayNumber(operations[i], 2.0);
                    ProcessAndDisplayNumber(operations[i], 7.94);
                    ProcessAndDisplayNumber(operations[i], 1.414);
                    Console.WriteLine();
                  }
                }
    
                static void ProcessAndDisplayNumber(DoubleOp action, double value)
                {
                  //在ProcessAndDisplayNumber中调用委托,执行委托实例引用的方法
                  double result = action(value);
                  Console.WriteLine(
                     "Value is {0}, result of operation is {1}", value, result);
                }
              }

    4.Action<T>和Func<T>委托
      除了为每个参数和返回类型定义一个委托类型之外,还可以使用Action<T>和Func<T>委托。
      泛型Action<T>委托表示引用一个void返回类型的方法。这个委托类存在不同的变体,可以传递最多16种不同的参数类型。没有泛型参数的Action类调用没有参数的方法。Action<in T>调用带一个参数的方法,Action<in T1,in T2>调用带两个参数的方法,依次类推。
      Func<T>委托允许调用带返回类型的方法。与Action<T>类似,Func<T>也存在不同的变体,可以传递最多16种不同的参数类型和一个返回类型。Func<out TResult>委托类型可以调用无参数且带返回类型的方法。

      下面使用Func<T>委托实现一个不使用委托很难编写的一个功能:给对象数组排序,如果对象是int或string这样值类型的对象会容易排序,如果是要排序很多自定义的类型的对象,需要编写大量代码。使用委托会减少代码量。
      定义包含比较方法的类:
      BubbleSorter类实现了一个泛型方法 Sort<T>,第一个参数是要排序的对象数组,第二个是一个委托,传递比较两个对象的方法。这样可以给Sort<T>方法,传递自定义的比较方法。

      

               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);
    
    
                }
              }

      定义自定义的一个类

          class Employee
              {
                public Employee(string name, decimal salary)
                {
                  this.Name = name;
                  this.Salary = salary;
                }
    
                public string Name { get; private set; }
                public decimal Salary { get; private set; }
    
                public override string ToString()
                {
                  return string.Format("{0}, {1:C}", Name, Salary);
                }
    
                public static bool CompareSalary(Employee e1, Employee e2)
                {
                  return e1.Salary < e2.Salary;
                }
              }

      客户端代码:

      

           Employee[] employees =
              {
                new Employee("Bugs Bunny", 20000),
                new Employee("Elmer Fudd", 10000),
                new Employee("Daffy Duck", 25000),
                new Employee("Wile Coyote", 1000000.38m),
                new Employee("Foghorn Leghorn", 23000),
                new Employee("RoadRunner", 50000)
              };
                //Sort执行了自定义的Employee.CompareSalary方法
              BubbleSorter.Sort(employees, Employee.CompareSalary);
    
              foreach (var employee in employees)
              {
                Console.WriteLine(employee);
              }

    5.多播委托
      前面介绍的每个委托只包含一个方法的调用,委托也可以包含多个方法。这种委托称为多播委托。
      如果调用多播委托,就可以按顺序调用多个方法,但如果委托的签名不是返回void,就只能得到委托调用的最后一个方法的结果。
      使用+=添加方法,-=删除方法。

      

        static void Main()
            {
              Action<double> operations = MathOperations.MultiplyByTwo;
              operations += MathOperations.Square;
    
              ProcessAndDisplayNumber(operations, 2.0);
              ProcessAndDisplayNumber(operations, 7.0);
              Console.WriteLine();
            }
    
            static void ProcessAndDisplayNumber(Action<double> action, double value)
            {
              Console.WriteLine();
              Console.WriteLine("ProcessAndDisplayNumber called with value = {0}", value);
              action(value);
    
            }
        
        
            class MathOperations
              {
                public static void MultiplyByTwo(double value)
                {
                  double result = value * 2;
                  Console.WriteLine("Multiplying by 2: {0} gives {1}", value, result);
                }
    
                public static void Square(double value)
                {
                  double result = value * value;
                  Console.WriteLine("Squaring: {0} gives {1}", value, result);
                }
              }

      每次调用ProcessAndDisplayNumber方法,都会按顺序调用action委托实例中的两个方法。
      输出:
      ProcessAndDisplayNumber called with value = 2
      Multiplying by 2: 2 gives 4
      Squaring: 2 gives 4

      ProcessAndDisplayNumber called with value = 7
      Multiplying by 2: 7 gives 14
      Squaring: 7 gives 49

      委托还可以使用+,-运算符:
      Action<double> operations1 = MathOperations.MultiplyByTwo;
      Action<double> operations2 = MathOperations.Square;
      Action<double> operations = operations1 + operations2;
      operations = operations - operations2;


      多播委托包含一个逐个调用的委托集合。如果其中一个方法抛出异常,整个迭代就会停止。
      static void One()
      {
        Console.WriteLine("One");
        throw new Exception("Error in one");
      }

      static void Two()
      {
        Console.WriteLine("Two");
      }


      static void Main()
      {
        Action d1 = One;
        d1 += Two;

        try
        {
          d1();
        }
        catch (Exception)
        {
          Console.WriteLine("Exception caught");
        }


      }
      委托只调用了第一个方法。因为第一个方法抛出异常,委托的迭代停止,不再调用Two()方法。

      避免这个问题,可以使用Delegate类定义的GetInvocationList()方法,它返回一个Delegate对象数组:
      Action d1 = One;
      d1 += Two;

      Delegate[] delegates = d1.GetInvocationList();
      foreach (Action d in delegates)
      {
        try
        {
          d();
        }
        catch (Exception)
        {
          Console.WriteLine("Exception caught");
        }
      }

      输出:
      One
      Exception caught
      Two

      使用GetInvocationList()方法可以为委托的每个方法传递不同的参数,获取每个方法的返回值。
      static int One(int x)
      {
        return x;
      }

      static int Two(int x)
      {
        return x;
      }


      static void Main()
      {

        Func<int,int> d1 = One;
        d1 += Two;

        Delegate[] delegates = d1.GetInvocationList();
        Func<int, int> d2 = (Func<int, int>)delegates[0];
        Console.WriteLine( d2(1));

        Func<int, int> d3 = (Func<int, int>)delegates[1];
        Console.WriteLine(d3(2));

        Console.ReadKey();
      }
      输出:
      1
      2

    6.匿名方法
      使用匿名方法可以将方法体直接赋给委托实例,而不需要定义一个方法。
      static void Main()
      {
        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"));

      }
      上面代码不是把方法名赋给委托变量anonDel,而是一段代码,它前面是关键字delegate和参数列表。在使用匿名方法时,可以使用外部变量。
      匿名方法的优点是减少了代码量。使用匿名方法,代码执行速度并没有加快。编译器仍定义了一个方法,该方法只有一个自动指定的名称。

      使用匿名方法,必须遵守两条规则:
      (1).在匿名方法中不能使用跳转语句(break,goto,continue)调到匿名方法的外部,外部的代码也不能调到匿名方法内部。
      (2).匿名方法内部不能访问不安全的代码。也不能在匿名方法使用ref和out

      如果需要匿名方法多次编写同一个功能时,就不要用匿名方法了。

  • 相关阅读:
    Js全选 添加和单独删除
    H5新手快速入门 简单布局
    DOM 节点 课程表
    Datalogic组网模式下通讯
    svn检出的时候报 Unable to connect to a repository at URL错误(摘自CSDN)
    Subsonic使用中
    HTTP 错误 500.21
    WinForm下增加声音提示
    IIS7.0发布Web服务器0002
    IIS7.0发布Web服务-0001
  • 原文地址:https://www.cnblogs.com/afei-24/p/6762442.html
Copyright © 2011-2022 走看看