前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >.Neter所应该彻底了解的委托

.Neter所应该彻底了解的委托

作者头像
ryzenWzd
发布于 2020-04-29 00:31:30
发布于 2020-04-29 00:31:30
41200
代码可运行
举报
文章被收录于专栏:WPFWPF
运行总次数:0
代码可运行

本文将通过引出几个问题来,并且通过例子来剖析C#中的委托以及用法,做抛砖引玉的作用

对于委托我发现大部分人都有以下问题,或者可能在面试中遇过这样的:

  • 委托是不是相当于C/C++的函数指针?
  • 委托究竟是什么?
  • 委托究竟是用来干嘛的?
  • 委托跟匿名函数的区别?
  • 委托与事件的关系?

我们先来声明和使用C++的函数指针: 代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>
using namespace std;

typedef int (*Foohandle)(int a,int b);

int fooMenthod(int a, int (*foohandle1)(int a,int b)) //回调函数
{
  return a + (*foohandle1)(2,3);//也可以写成foohandle1(2,3)
}

int add(int a,int b) {
  return a + b;
}

int multiply(int a, int b) {
  return a * b;
}

int main()
{
 Foohandle foohandle = add;
 int (*foohandle1)(int a, int b) = &add;
 cout << foohandle(2,3)<<endl;
 cout << foohandle1(2,3) << endl;
 cout << typeid(Foohandle).name() << endl;
 cout << typeid(foohandle).name()<<endl;
 cout << typeid(foohandle1).name() << endl;
 cout << fooMenthod(2, add)<<endl;
 cout << fooMenthod(2, multiply);
}

输出结果如下:

    在代码中,我声明定义了两个函数add和multiply,然后用typedef方式声明了函数指针,接着我分别将add赋值给Foohandle这种函数指针类型foohandle变量,然后用&add这种解地址的方式赋值给一个返回值为int,且带有两个参数的函数指针foohandle1,其中(*foohandle1)是函数名,最后我输出发现它们类型和输出都是一致的,再后面,我们定义了一个fooMenthod函数,返回值是int,且其中一个参数是函数指针,那么我再最后调用两次,分别将add和multiply函数,赋值给它,这时候add和multiply就是fooMenthod函数的回调函数,且此时输出结果会被两个函数内部不同实现所影响 那么我们可以做个总结:

  • 首先函数指针就是一个内存地址,指向函数的入口内存地址
  • 当函数指针做一个函数的参数时,确实会起到一定解耦作用
  • 函数指针很明显是类型不安全的

我们再来声明和使用委托:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public delegate int Foohandle(int a, int b);
class Program
{
static void Main(string[] args)
{
 Foohandle foohandle = new Foohandle(add);
 Console.WriteLine(foohandle(2, 3));
 Console.WriteLine(foohandle.GetType().Name);
 Console.WriteLine(fooMenthod(2, add));
 Console.WriteLine(fooMenthod(2, multiply));
 Console.WriteLine($"foohandle所调用函数函数名:{foohandle.Method.Name}");
 Console.WriteLine($"foohandle所调用函数的返回值类型{foohandle.Method.ReturnType.ToString()}");
 Console.WriteLine("foohandle所调用函数参数类型以及参数名分别为:");
 Console.WriteLine($"Type:{foohandle.Method.GetParameters()[0].ParameterType},Name:{foohandle.Method.GetParameters()[0].Name}");
 Console.WriteLine($"Type:{foohandle.Method.GetParameters()[1].ParameterType},Name:{foohandle.Method.GetParameters()[1].Name}");
 Console.Read();
}

static int fooMenthod(int a, Foohandle foohandle) //传给参数函数的就是回调函数
{
 return a + foohandle(2, 3);
}

static int add(int a, int b)
{
 return a + b;
}

static int multiply(int a, int b)
{
 return a * b;
}
}

输出结果:

    很明显,不管是声明和使用方式,都和c++那边一样,就连输出结果也差不多,但是很有意思的是,foohandle的类型是Foohandle,且我居然能从foohandle输出所调函数的一切信息,包括函数名,返回值,参数类型和参数名,而且和c++那边不同的是,我们没有直接操作内存地址,好像看起来是安全的?那么Foohandle类型又是什么?

委托是啥?

先来个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace DelegateSample
{

  public delegate void FooHandle(int value);

  class Program
  {
     static void Main(string[] args)
     {
        FooHandle fooHandle = new FooHandle(multiply);
        fooHandle(3);
        Console.WriteLine($"fooHandle.Target:{fooHandle.Target},fooHandle.Method:{fooHandle.Method},fooHandle.InvocationListCount:{fooHandle.GetInvocationList().Count()}");
        Console.WriteLine("-----------------------------------------------------------------------------------------------------------------------------------");
        FooHandle fooHandle1 = new FooHandle(new Foo().Add);
        fooHandle1.Invoke(3);
        Console.WriteLine($"fooHandle1.Target:{fooHandle1.Target},fooHandle1.Method:{fooHandle1.Method},fooHandle1.InvocationListCount:{fooHandle1.GetInvocationList().Count()}");
        Console.Read();
     }

     static void multiply(int a)
     {
        Console.WriteLine(a*2);
     }
    }

   public class Foo
   {
      public void Add(int value)
      {  
        Console.WriteLine(value + 2);
      }
   }
}

我们看看输出的结果:

     很明显,这里是一个最简单的委托声明,实例化初始化一个委托对象,然后调用的最简单的场景      我们不关注输出的第一行,很明显,对象实例化后,可以访问其中的三个公开public的函数成员, 分别是Target(object类型),Method(MethodInfo类型),而GetInvocationList函数是一个返回值为一个Delegate[]的无参函数      在上面代码,其实我还特地将委托FooHandle声明在Program类外面,其实在这里我们已经知道委托是什么了,实例化对象,且能够声明在类外面,其实它本质就是一个类,我们通过反编译来验证:

大概是这样,伪代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class FooHandle: MulticastDelegate
{
  public FooHandle(object @object,IntPtr menthod);//构造方法

  void Invoke(int value)//调用委托,编译后公共语言运行时给delegate提供的特殊方法

  void EndInvoke(System.IAsyncResult asyncResult)// 编译后公共语言运行时给MulticastDelegate提供的特殊方法

  // 编译后公共语言运行时给MulticastDelegate提供的特殊方法
  void BeginInvoke(int value,System.AsyncCallback callback, object obj) 
}

我们可以看编译后FooHandle就是一个类,且继承MulticastDelegate,且继承链关系在msdn是这样的:

    且我们发现上面公开的三个函数成员都来自于Delegate类,且编译后生成了几个公共运行时提供的特殊方法,Invoke方法我们很清楚,是来调用委托的,我们先来看看委托初始化后的情况,通过查看Delegate的源码,我们发现Delegate有两个构造函数:

1.委托对象初始化构造函数是实例函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[SecuritySafeCritical]
protected Delegate(object target, string method)
{
  if (target == null)
  {
    throw new ArgumentNullException("target");
  }
  if (method == null)
  {
    throw new ArgumentNullException("method");
  }
  if (!BindToMethodName(target, (RuntimeType)target.GetType(), method, (DelegateBindingFlags)10))
  {
    throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTargMeth"));
  }
} 

2.委托对象初始化构造函数是静态函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[SecuritySafeCritical]
protected Delegate(Type target, string method)
{
  if (target == null)
  {
    throw new ArgumentNullException("target");
  }
  if (target.IsGenericType && target.ContainsGenericParameters)
  {
    throw new ArgumentException(Environment.GetResourceString("Arg_UnboundGenParam"), "target");
  }
  if (method == null)
  {
    throw new ArgumentNullException("method");
  }
  RuntimeType runtimeType = target as RuntimeType;
  if (runtimeType == null)
  {
    throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"), "target");
  }
  BindToMethodName(null, runtimeType, method, (DelegateBindingFlags)37);
}

最后共同调用的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//调用CLR的内部代码
[MethodImpl(MethodImplOptions.InternalCall)]
[SecurityCritical]
private extern bool BindToMethodName(object target, RuntimeType methodType, string method, DelegateBindingFlags flags);

    虽然我们看不到BindToMethodName方法的实现,已经很明显了,委托对象初始化构造函数是静态函数传参进去BindToMethodName的第一个object的target参数为null,那我们大概把之前的伪代码的构造函数这么实现了:

伪代码部分:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
internal object _target//目标对象;
internal IntPtr _methodPtr//目标方法;
internal IntPtr _methodPtrAux//用来判断Target是否为空;

//foolHandle的构造方法实现:
public FooHandle(object @object,IntPtr menthod)
{
  _methodPtr=menthod;//multiply
  _methodPtrAux=1;//只要不等于nul

}

//foolHandle1的构造方法实现:
public FooHandle(object @object,IntPtr menthod)
{
  _methodPtr=menthod//Add
  _methodPtrAux=0//为null
  _target=foo;

}

Delegate Target属性源代码部分:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[__DynamicallyInvokable]
public object Target
{
  [__DynamicallyInvokable]
  get
  {
    return GetTarget();
  }
}

[SecuritySafeCritical]
internal virtual object GetTarget()
{
  if (!_methodPtrAux.IsNull())
  {
    return null;
  }
  return _target;
}

    而获取Method的方法就不展开了,就是通过反射来获取,那我们已经知道Target和Method属性究竟是怎么回事了,我们还发现没讲到GetInvocationList方法是怎么回事?我们知道委托是支持多播委托的,也就是大概这样,修改上述代码为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace DelegateSample
{
   public delegate void FooHandle(int value);

   class Program
   {
      static void Main(string[] args)
      {
        FooHandle fooHandle = new FooHandle(multiply);
        fooHandle(3);
        Console.WriteLine($"fooHandle.Target:{fooHandle.Target},fooHandle.Method:{fooHandle.Method},fooHandle.InvocationListCount:{fooHandle.GetInvocationList().Count()}");
        Console.WriteLine("----------------------------------------------------------------------------------------------------------------");
        FooHandle fooHandle1 = new FooHandle(new Foo().Add);
        fooHandle1.Invoke(3);
        Console.WriteLine($"fooHandle1.Target:{fooHandle1.Target},fooHandle1.Method:{fooHandle1.Method},fooHandle1.InvocationListCount:{fooHandle1.GetInvocationList().Count()}");
        Console.WriteLine();


        Console.WriteLine("--------------------------------------------------新增代码------------------------------------------------------");
        FooHandle fooHandle2 = new FooHandle(new Program().Minus);
        Console.WriteLine($"fooHandle2.Target:{fooHandle2.Target},fooHandle1.Method:{fooHandle2.Method},fooHandle1.InvocationListCount:{fooHandle2.GetInvocationList().Count()}");
        fooHandle2(2);
        Console.WriteLine("----------------------------------------------------------------------------------------------------------------");
        FooHandle fooHandle3 = null;
        fooHandle3 += fooHandle;
        fooHandle3 =(FooHandle)Delegate.Combine(fooHandle3,fooHandle1);//相当于fooHandle3+=fooHandle1;
        fooHandle3 += new Program().Minus;
        Console.WriteLine($"fooHandle3.Target:{fooHandle3.Target},fooHandle3.Method:{fooHandle3.Method},fooHandle3.InvocationListCount:{fooHandle3.GetInvocationList().Count()}");
        fooHandle3(2);
        foreach (var result in fooHandle3.GetInvocationList())
        {
           Console.WriteLine($"result.Target:{result.Target},result.Method:{result.Method},result.InvocationListCount:{result.GetInvocationList().Count()}");
        } 
        Console.Read();
    }


   private void Minus(int a)
   {
     Console.WriteLine(a-1);
   }

   static void multiply(int a)
   {
     Console.WriteLine(a * 2);
   }
 }

 public class Foo
 {
    public void Add(int value)
    {
       Console.WriteLine(value + 2);
    }
 }
}

输出结果是:

    上面新增的代码,我声明了一个新的委托变量fooHandle3初始化为null,接着分别用三种不同的方式将委托或者函数加给fooHandle,之后输出后相当于分别按序调用输出了三个方法,而我们遍历其中的fooHandle3.GetInvocationList()委托数组,输出的也确实三个方法,但是注意到了没,我在fooHandle3 += new Program().Minus这段确实没有声明一个委托变量,我们可以注意到其中的(FooHandle)Delegate.Combine(fooHandle3,fooHandle1)这句,Combine很明显是需要两个委托变量的,查看编译后的代码我们可以得知到底发生了啥? Il关键代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//fooHandle3 += fooHandle
IL_00f7: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_00fc: castclass DelegateSample.FooHandle
IL_0101: stloc.3
IL_0102: ldloc.3
IL_0103: ldloc.1
//fooHandle3 =(FooHandle)Delegate.Combine(fooHandle3,fooHandle1)
IL_0104: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_0109: castclass DelegateSample.FooHandle
IL_010e: stloc.3
IL_010f: ldloc.3
//new Program()
IL_0110: newobj instance void DelegateSample.Program::.ctor()
IL_0115: ldftn instance void DelegateSample.Program::Minus(int32)
//new FooHandle()新增了一个FooHandle委托变量
IL_011b: newobj instance void DelegateSample.FooHandle::.ctor(object,
native int)
//fooHandle3 += new Program().Minus 
IL_0120: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)

     也就是三种不同方式都会被翻译为Combine方法,如果是直接+=函数这种情况,后台也会new一个委托变量,将方法赋值给该变量再加到fooHandle3,那么我们可以知道,最关键的核心代码就应该是Delegate.combine这个静态方法了,我们来看看源码是怎么回事: Delegate类的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[__DynamicallyInvokable]
public static Delegate Combine(Delegate a, Delegate b)
{
   if ((object)a == null)
   {
     return b;
   }
   return a.CombineImpl(b);
}

protected virtual Delegate CombineImpl(Delegate d)
{
   throw new MulticastNotSupportedException(Environment.GetResourceString("Multicast_Combine"));
}

MulticastDelegate类的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[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方法的实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//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/C++的函数指针?

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

  • 委托是什么?

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

  • 委托是用来做啥的?

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

  • 委托跟匿名函数的区别?

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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,后台都会实例化一个新的委托对象,并把该表达式作为函数赋给它

  • 委托与事件的关系?

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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); 
   }
}

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
4
EventSample.Foo,Void add(Int32, Int32)
4
4

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

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

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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();
    }

}

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Hello!
I am Func

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-12-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
ch01.深入理解C#委托及原理_《没有控件的ASPDONET》
设想,如果我们写了一个厨师做菜方法用来做菜,里面有 拿菜、切菜、配菜、炒菜 四个环节,但编写此方法代码的人想让 配菜 这个环节让调用方法的人实现,换句话说,就是想在方法被调用时接收代码 作为参数,在方法中执行这端传进来的代码。
Edison Zhou
2018/08/20
9790
ch01.深入理解C#委托及原理_《没有控件的ASPDONET》
.NET基础拾遗(4)委托、事件、反射与特性
  委托这个概念对C++程序员来说并不陌生,因为它和C++中的函数指针非常类似,很多码农也喜欢称委托为安全的函数指针。无论这一说法是否正确,委托的的确确实现了和函数指针类似的功能,那就是提供了程序回调指定方法的机制。
Edison Zhou
2018/08/20
1K0
.NET基础拾遗(4)委托、事件、反射与特性
委托与事件
  首先多谢网上的大牛们无私地分享自己的经验,让我这样的小鸟能站在巨人的肩膀上不断前进!   参考网址:http://www.knowsky.com/395937.html(参考1) http://hi.baidu.com/zhangguilin/blog/item/9d34dc17a088ff0ac83d6d08.html(参考2) http://www.chinaz.com/program/2009/1014/94676.shtml(参考3)   以下是我的总结: 委托              
^_^肥仔John
2018/01/18
8900
C# 委托基础
不知不觉,又开始委托的学习了,感觉这个东西我能学一辈子,哈哈哈!这次看的是官方的参考书(C#高编9),每次看不同的资料,总能学到不同的知识!言归正传 1、为什么要使用委托? 我们习惯于将数据作为参数传
郑小超.
2018/01/26
6560
[C#1] 10-事件
事件概述 CLR的事件模型建立在委托的机制之上。定义事件成员的类型允许类型(或者类型的实例)在某些特定事件发生时通知其他对象,事件为类型提供了一下三种能力: 1允许对象登记该事件 2允许对象注销该事件 3允许定义事件的对象维持一个登记对象的集合,并在某些特定的事件反生时通知这些对象 下面是根据一个上课的场景解释事件的原理【上课铃响,老师讲课,学生进教室听课】。定义一个RingManager类管理上课铃声,定义一个SchoolBell【上课铃响】的事件,Teacher和Student类型登记该事件。当Teac
blackheart
2018/01/19
5110
C#基础篇 - 理解委托和事件
委托类似于C++中的函数指针(一个指向内存位置的指针)。委托是C#中类型安全的,可以订阅一个或多个具有相同签名方法的函数指针。简单理解,委托是一种可以把函数当做参数传递的类型。很多情况下,某个函数需要动态地去调用某一类函数,这时候我们就在参数列表放一个委托当做函数的占位符。在某些场景下,使用委托来调用方法能达到减少代码量,实现某种功能的用途。
Esofar
2018/09/05
5360
C#基础篇 - 理解委托和事件
[C#1] 9-委托
委托揭秘 编译器和CLR在后台做了很多工作来隐藏委托本身的复杂性,如下一句委托声明: //编译器为我们产生了一个同名的类 public delegate void MyDelegate(int i);
blackheart
2018/01/19
7900
[C#1] 9-委托
C#中的委托delegate
委托是一种定义方法签名的类型,可以与具有兼容签名的任何方法关联。你可以通过委托调用方法。委托用于将方法参数传递给其它方法。事件处理程序就是通过委托调用的方法(这是MSDN中对委托的解释)。个人感觉C#中委托类似于C++中的函数指针,但委托又是一种特殊的类,只有方法的类。
卡尔曼和玻尔兹曼谁曼
2019/01/25
9910
C#中的委托delegate
C#中的委托和事件 - Part.2
如果你看过了 C#中的委托和事件 一文,我想你对委托和事件已经有了一个基本的认识。但那些远不是委托和事件的全部内容,还有很多的地方没有涉及。本文将讨论委托和事件一些更为细节的问题,包括一些大家常问到的问题,以及事件访问器、异常处理、超时处理和异步方法调用等内容。
张子阳
2018/09/27
2.1K0
事件与委托(一)
委托的定义:委托类型定义了委托实例可以调用的那类方法,具体来说,委托类型定义了方法的返回类型和参数。
JusterZhu
2022/12/07
4540
[C#] 委托练习
委托变量可以在运行时指定一个目标方法,这个特性可用于编写插件方法。在本例中有一个名为Transform的公共方法,它对整数数组的每一个元素进行变换。Transform方法接受一个委托参数并以此为插件方法执行变换操作:
科控物联
2022/03/29
5210
[C#] 委托练习
C#委托的介绍(delegate、Action、Func、predicate)
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递。事件是一种特殊的委托。
全栈程序员站长
2022/07/05
4160
C#委托
在C#中的委托关键字是 Delegate,委托类似于C/C++中函数的指针。是存有对某个方法引用的引用类型变量,可在运行时被改变。一般用于实现事件和回调方法。
喵叔
2020/09/08
5680
C#委托
[读书笔记]C#学习笔记二: 委托和事件的用法及不同.
前言:  C#委托是什么     c#中的委托可以理解为函数的一个包装, 它使得C#中的函数可以作为参数来被传递, 这在作用上相当于C++中的函数指针. C++用函数指针获取函数的入口地址, 然后通过这个指针来实现对函数的操作. 委托的定义和方法的定义类似, 只是在定义的前面多了一个delegate关键字. 正文: 委托可以被视为一个更高级的指针,它不仅仅能把地址传指向另一个函数,而且还能传递参数,返回值等多个信息。 系统还为委托对象自动生成了同步,异步的调用方式,开发人员使用BeginInvok
一枝花算不算浪漫
2018/05/18
6440
.NET面试题系列[7] - 委托与事件
C#中的委托可以说俯拾即是,从LINQ中的lambda表达式到(包括但不限于)winform,wpf中的各种事件都有着委托的身影。C#中如果没有了事件,那绝对是一场灾难,令开发者寸步难行。而委托又是事件的基础,可以说是C#的精髓,个人认为,其地位如同指针之于C语言。
s055523
2018/09/14
1.2K0
.NET面试题系列[7] - 委托与事件
C#学习笔记 委托
有时候可能想要将一个方法传递给另一个方法。在C++中使用函数指针来实现,在JavaScript中由于函数也是对象所以直接可以在参数列表中传递。而在C#中需要使用委托。
乐百川
2022/05/05
2760
『.Net反射』ILGenerator.Emit 动态MSIL 编程
--------------------------------------------------------------------------
yaphetsfang
2020/07/30
4100
.NET面试题解析(05)-常量、字段、属性、特性与委托
上面一段非常简单代码,其生产的IL代码如下,在使用常量变量的地方,把她的值拷过来了(把常量的值内联到使用的地方),与常量变量A.PORT没有关系了。假如A引用了B程序集(B.dll文件)中的一个常量,如果后面单独修改B程序集中的常量值,只是重新编译了B,而没有编译程序集A,就会出问题了,就是上面所说的不支持跨程序集版本更新。常量值更新后,所有使用该常量的代码都必须重新编译,这是我们在使用常量时必须要注意的一个问题。
莫问今朝
2018/08/31
8390
.NET面试题解析(05)-常量、字段、属性、特性与委托
C#中的委托解析
该文章主要介绍了.NET Core中的Delegate类型以及如何使用Delegate对方法进行调用。主要包括了Delegate的定义、创建、调用以及Delegate的回收等操作。在.NET Core中,Delegate是用于实现多播委托的一种方式,它可以用于调用多个方法,并且能够处理异常,在.NET Core中,Delegate是安全调用委托的方式,使用Delegate可以避免因为直接调用方法而导致的异常。
彭泽0902
2018/01/04
1.7K0
理解委托类型
delegate 是一种可用于封装命名或匿名方法的引用类型。(并不是所有的引用类型都是class)
lulianqi
2018/09/07
7280
理解委托类型
相关推荐
ch01.深入理解C#委托及原理_《没有控件的ASPDONET》
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文