VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > Python基础教程 >
  • .Neter所应该彻底了解的委托(2)

复制代码

 

MulticastDelegate类的:

复制代码
[SecurityCritical]
private object _invocationList;//委托链表

[SecurityCritical]
private IntPtr _invocationCount;

[SecuritySafeCritical]
protected sealed override Delegate CombineImpl(Delegate follow)
{
   if ((object)follow == null)
   {
      return this;
   }
   if (!Delegate.InternalEqualTypes(this, follow))
   {
     throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTypeMis"));
   }
   MulticastDelegate multicastDelegate = (MulticastDelegate)follow;
   int num = 1;
   object[] array = multicastDelegate._invocationList as object[];
   if (array != null)
   {
       num = (int)multicastDelegate._invocationCount;
   }
   object[] array2 = _invocationList as object[];
   int num2;
   object[] array3;
   if (array2 == null)
   {
     num2 = 1 + num;
     array3 = new object[num2];
     array3[0] = this;
     if (array == null)
     {
        array3[1] = multicastDelegate;
     }
     else
     {
       for (int i = 0; i < num; i++)
       {
         array3[1 + i] = array[i];
       }
     }
     return NewMulticastDelegate(array3, num2);
    }
    int num3 = (int)_invocationCount;
    num2 = num3 + num;
    array3 = null;
    if (num2 <= array2.Length)
    {
       array3 = array2;
       if (array == null)
       {
           if (!TrySetSlot(array3, num3, multicastDelegate))
           {
             array3 = null;
           }
       }
       else
       {
         for (int j = 0; j < num; j++)
         {
            if (!TrySetSlot(array3, num3 + j, array[j]))
            {
               array3 = null;
               break;
            }
         }
       }
    }
    if (array3 == null)
    {
       int num4;
       for (num4 = array2.Length; num4 < num2; num4 *= 2)
       {
       }
       array3 = new object[num4];
       for (int k = 0; k < num3; k++)
       {
          array3[k] = array2[k];
       }
       if (array == null)
       {
          array3[num3] = multicastDelegate;
       }
       else
       {
          for (int l = 0; l < num; l++)
          {
             array3[num3 + l] = array[l];
          }
       }
     }
     return NewMulticastDelegate(array3, num2, thisIsMultiCastAlready: true);
   }
复制代码

 

GetInvocationList方法的实现:

复制代码
//Delgate类的
public virtual Delegate[] GetInvocationList()
{
   return new Delegate[1]
   {
     this
   };
}

//MulticastDelegate类的
public sealed override Delegate[] GetInvocationList()
{
   object[] array = _invocationList as object[];
   Delegate[] array2;
   if (array == null)
   {
     array2 = new Delegate[1]
     {
       this
     };
   }
   else
   {
     int num = (int)_invocationCount;
     array2 = new Delegate[num];
     for (int i = 0; i < num; i++)
     {
        array2[i] = (Delegate)array[i];
     }
   }
   return array2;
}
复制代码

 

    其实我们看到这里,就可以知道其中的一个最主要就是_invocationList变量,也就是当调用Combine的时候,会判断左边委托变量是否为空,如果为空,会返回右边的委托变量,不为空就会调用CombineImpl方法,以上面那个例子来说fooHandle3_invocationList存储着所有附加到委托变量,包含对象本身,也就是为啥遍历fooHandle3.GetInvocationList,输出了三个附加到fooHandle3变量的委托变量,这里例子fooHandle3初始化为null,还有意思的是fooHandle3的Targt和Menthod属性是最后附加的那个委托变量的Target和Menthod,而当委托由返回值,也同理返回最后一个函数的返回值,那么fooHandle3大概的结构如下图:

 

     我们到现在只用到+=,其实-=就是调用其Delegate.Remove方法,跟Combine方法作用相反,具体就不多概述
看到这里我们终于可以回答一开头抛出的几个问题?

          很明显,不是的,从数据结构来说,c++函数指针表示一块指向函数的内存地址,它其实和直接写函数名没啥区别,因为我们调用函数时的函数名,也是函数入口地址,而委托却是个类,是一块托管内存,使用Invoke后它就会被clr释放了,它的函数成员能够存储所调函数的所有信息,这是函数指针没做到的,但是在某些特殊情况下,C++的函数指针就和委托一样,有兴趣的朋友可以去看下p/invoke方面知识

          委托本质是类,且支持多播委托的本质是维护一个私有的_invocationList委托链对象,+=和-=都是调用其静态方法Combine和Remove

          委托和c++函数指针一样,都可以作为函数中转器,在调用者和被调用者中起解耦作用,可作为函数的参数,当回调函数

         我们先来声明和使用匿名函数:

复制代码
public delegate int Foohandle(int a, int b);

Foohandle foohandle = delegate (int a, int b) { return a + b; };//匿名方法方式
Foohandle foohandle1= (a, b)=> a + b;//Lambda 表达式方式

foohandle.Invoke(2,2);//输出4
foohandle1.Invoke(2,2);//输出4
复制代码

 

我们来看下msdn是怎么定义匿名函数的:

 

很明显,匿名函数只是个表达式,可以用来初始化委托的,而委托是个类,其实通过查看IL,后台都会实例化一个新的委托对象,并把该表达式作为函数赋给它

 同样的我们来声明和使用事件:

复制代码
public class Foo
{
   public delegate void Foohandel(int a, int b);

   public event Foohandel foohandle;

   public Foo()
   {
      foohandle = new Foohandel(add);
      foohandle(2,2);//在Foo里面可以直接调用事件
      Console.WriteLine($"{foohandle.Target},{foohandle.Method}");
   }

   public void excute(int a,int b)//公开给外部类调用事件的函数
   {
      foohandle?.Invoke(a,b);
   }

   private void add(int a, int b)
   {
      Console.WriteLine(a + b); 
   }
}

class Program
{
   static void Main(string[] args)
   {
      Foo foo = new Foo();
      //foo.foohandle = new Foo.Foohandel(multiply);编译不过,提示foo.foohandle只能出现再+=和-=左边
      foo.foohandle +=new Foo.Foohandel(multiply);
      foo.excute(2, 2); 
      Console.Read();
   }

   static void multiply(int a,int b)
   {
      Console.WriteLine(a * b); 
   }
}
复制代码

 

输出结果:

4
EventSample.Foo,Void add(Int32, Int32)
4
4

 

     我们发现,在Foo类里面,事件foohandle就是相当于委托,但是在外部,我们再program的main函数访问它时候,我们发现foohandle只能做+=或者-=,也不能访问其函数成员Target和Menthod,而我们只能通过调用excute函数去调用,这时候我们可以知道,Event其实是基于委托的,在内部类相当于委托,在外部就只能有委托的多播功能,其余都不能访问,其实我们想到,属性是不是这样。。。有兴趣的朋友可以去了解事件的原理,也是很有趣


最后的最后,我们还要谈下委托的一个功能:

委托的参数逆变和返回值的协变#

由于委托也支持泛型委托,因此我们可以看看微软定义好的

复制代码
public delegate void Action<in T>(T obj);//其中in表示逆变
public delegate TResult Func<out TResult>();//其中out表示协变

class Program
{
    static Action<object> action;
    static Func<string> func;
    static void Main(string[] args)
    {
       action = (object a) => { Console.WriteLine(a.ToString()); };
       Action<string> action1 = action;//参数逆变
       action("Hello!");


       func = () => { return "I am Func"; };
       Func<object> func1 = func;//返回值协变
       Console.WriteLine(func1()); 
       Console.ReadLine();
    }

}
复制代码

 

输出结果:

Hello!
I am Func

想要了解更深的朋友可以去了解泛型的协变和逆变,在这里就不深入探讨了

 

委托的参数逆变和返回值的协变
[Wěituō de cānshù nì biàn hé fǎnhuí zhí de xié biàn]
Covariant return parameters of the inverter and a delegate value

作者: RyzenAdorer



相关教程
关于我们--广告服务--免责声明--本站帮助-友情链接--版权声明--联系我们       黑ICP备07002182号