c#委托详解和和示例分享

 更新时间:2014年03月23日 12:09:49   作者:  
这篇文章详细探讨了C#中的委托,列举其主要的实现方式,并分析其在设计层面和编码层面带来的好处,最后会讨论其安全性和执行效率等,当然还有实现示例
什么是委托?

委托是寻址方法的.NET版本,使用委托可以将方法作为参数进行传递。委托是一种特殊类型的对象,其特殊之处在于委托中包含的只是一个活多个方法的地址,而不是数据。

委托虽然看起来像是一种类型,但其实定义一个委托,是定义了一个新的类。下面这行代码,定义了一个委托,使用ILDasm.exe查看其生成的IL代码如图所示:

复制代码 代码如下:

//定义委托,它定义了可以代表的方法的类型,但其本身却是一个类
 public delegate int methodDelegate(string str);

032101

由图中红色框线中可以看出,.NET将委托定义为一个密封类,派生自基类System.MulticastDelegate,并继承了基类的三个方法(稍后讨论这三个)。

委托与函数指针的区别

1、安全性:C/C++的函数指针只是提取了函数的地址,并作为一个参数传递它,没有类型安全性,可以把任何函数传递给需要函数指针的地方;而.NET中的委托是类型安全的。

2、与实例的关联性:在面向对象编程中,几乎没有方法是孤立存在的,而是在调用方法前通常需要与类实例相关联。委托可以获取到类实例中的信息,从而实现与实例的关联。

3、本质上函数指针是一个指针变量,分配在栈中;委托类型声明的是一个类,实例化为一个对象,分配在堆中。

4、委托可以指向不同类中具有相同参数和签名的函数,函数指针则不可以。

复制代码 代码如下:

namespace ConsoleApplication1
{
    //定义委托,它定义了可以代表的方法的类型,但其本身却是一个类
    public delegate void methodDelegate(string str);
    class Program
    {
        static void Main(string[] args)
        {
            Student student = new Student();
            Teacher teacher = new Teacher("王老师");
            methodDelegate methodDelegate1 = new methodDelegate(student.getStudentName);
            methodDelegate1 += teacher.getTeacherName; //可以指向不同类中的方法!
            //methodDelegate1 += teacher.getClassName; 指向签名不符的方法时提示错误!
            methodDelegate1.Invoke("张三");
            Console.ReadLine();
        }
    }

    class Student
    {
        private String name = "";
        public Student (String _name)
        {
            this.name = _name ;
        }
        public Student() {}
        public void getStudentName(String _name)
        {
            if (this.name != "" )
                Console.WriteLine("Student's name is {0}", this.name);
            else
                Console.WriteLine("Student's name is {0}", _name);
        }
    }

    class Teacher
    {
        private String name;
        public Teacher(String _name)
        {
            this.name = _name;
        }
        public void getTeacherName(String _name)
        {
            if (this.name != "")
                Console.WriteLine("Teacher's name is {0}", this.name);
            else
                Console.WriteLine("Teacher's name is {0}", _name);
        }
        public string getClassName()
        {
            return "Eanlish";
        }
    }
}

上述测试代码运行结果如下:

当指向签名不符的方法时会提示如下错误,证实了委托的安全性。

下面来看看C#中实现委托有哪些方式及各自主要适用范围。

1、常规实现

复制代码 代码如下:

private delegate String getAString();
static void Main(String []args)
{
    int temp = 40;
    getAString stringMethod = new getAString(temp.ToString);
    Console.WriteLine("String is {0}", stringMethod());//这里stringMethod()等价于调用temp.ToString();
    Console.ReadLine();
}


这段代码中,实例化了类型为GetAString的一个委托,并对它进行初始化,使它引用整型变量temp的ToString()方法。在C#中,委托在语法上总是接受一个参数的构造函数,这个参数就是委托引用的方法。上例中stringMethod()等价于使用temp.ToString(),同时也与调用委托类的Invoke()方法完全相同,实际上,如下图IL代码中红色部分所示,C#编译器会用stringMethod.Invoke()代替stringMethod()。

为了简便输入,C#支持只传送地址的名称给委托的实例(委托推断),如下两行代码在编译器看来是一样的。

复制代码 代码如下:

getAString stringMethod = new getAString(temp.ToString);
getAString stringMethod = temp.ToString;

实际上委托的实例可以引用任何类型的任何对象上的实例方法或静态方法,只要方法的签名匹配于委托的签名即可。所以结构体的方法一样可以传递给委托。

2、多播委托
多播委托具有一个带有链接的委托列表,称为调用列表,在对委托实例进行调用的时候,将按列表中的委托顺序进行同步调用。如果委托有返回值,则将列表中最后一个方法的返回值用作整个委托调用的返回值。因此,使用多播委托通常具有void返回类型。

可以使用+=来使委托指向多个方法的地址,但必须是在委托实例化之后才可以使用+=来添加新的方法地址(添加重复的方法地址编译器不会报错,但是也不会重复执行),若想移除其中的方法地址可以使用-=来实现(需要至少保留一个,即对于最后一个方法地址的移除不起作用)。以下代码中下面两行无论单独保留哪行,最终的执行结果都是相同的。

复制代码 代码如下:

getAString stringMethod = new getAString(temp.ToString);
stringMethod += temp.ToString;
stringMethod -= temp.ToString;
 

3、委托数组

复制代码 代码如下:

delegate double Operations(double x);

    class Program
    {
static void Main()
{
    Operations[] operations =
    {
       MathOperations.MultiplyByTwo,
       MathOperations.Square
    };

    for (int i = 0; i < operations.Length; i++)
    {
Console.WriteLine("Using operations[{0}]:", i);
DisplayNumber(operations[i], 2.0);
DisplayNumber(operations[i], 7.94);
Console.ReadLine();
    }
}

static void DisplayNumber(Operations action, double value)
{
    double result = action(value);
    Console.WriteLine(
       "Input Value is {0}, result of operation is {1}", value, result);
}
    }

    struct MathOperations
    {
public static double MultiplyByTwo(double value)
{
    return value * 2;
}

public static double Square(double value)
{
    return value * value;
}
    }

上述代码中实例化了一个委托数组operations(与处理类的实例相同),该数组的元素初始化为MathOperations类的不同操作,遍历这个数组,可以将每个操作应用到2个不同的值中。这种用法的好处是,可以在循环中调用不同的方法。

相关文章

  • C#中将ListView中数据导出到Excel的实例方法

    C#中将ListView中数据导出到Excel的实例方法

    首先 你需要添加引用Microsoft Excel 11.0 Object Library
    2013-04-04
  • c# BackgroundWorker使用方法

    c# BackgroundWorker使用方法

    这篇文章主要介绍了c# BackgroundWorker使用方法,文中代码非常详细,帮助大家更好的参考学习,感兴趣的朋友可以了解下
    2020-06-06
  • C# 实现Trim方法去除字符串前后的所有空格

    C# 实现Trim方法去除字符串前后的所有空格

    这篇文章主要介绍了C# 实现Trim方法去除字符串前后的所有空格,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 最好用的WPF加载动画功能

    最好用的WPF加载动画功能

    当开发应用程序时,提供良好的用户体验(UX)是至关重要的,加载动画作为一种有效的沟通工具,它不仅能告知用户系统正在工作,还能够通过视觉上的吸引力来增强整体用户体验,本文给大家介绍了最好用的WPF加载动画功能,需要的朋友可以参考下
    2025-01-01
  • C#客户端程序Visual Studio远程调试的方法详解

    C#客户端程序Visual Studio远程调试的方法详解

    这篇文章主要给大家介绍了关于C#客户端程序Visual Studio远程调试的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-09-09
  • C#跨窗体操作(引用传递) 实例代码

    C#跨窗体操作(引用传递) 实例代码

    现在给大家介绍一种最简单的跨窗体操作,WinForm的窗体是一个类,C#的类是引用类型,那么我们应该可以将WinForm窗体类进行传递,那不就可以进行操作了么?
    2013-03-03
  • C#解决文件被占用资源,无法删除或修改的方法

    C#解决文件被占用资源,无法删除或修改的方法

    这篇文章主要介绍C#解决文件被占用资源,比较实用,需要的朋友可以参考下。
    2016-06-06
  • 在C#使用字典存储事件示例及实现自定义事件访问器

    在C#使用字典存储事件示例及实现自定义事件访问器

    这篇文章主要介绍了在C#使用字典存储事件示例及实现自定义事件访问器的方法,是C#事件编程中的基础知识,需要的朋友可以参考下
    2016-02-02
  • 深入理解C#中常见的委托

    深入理解C#中常见的委托

    这篇文章主要介绍了C# 委托(Delegate)的相关资料,文中讲解非常详细,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下,希望能够帮助到你
    2021-07-07
  • C#实现异步操作的几种方式

    C#实现异步操作的几种方式

    在C#中,异步操作可以提高程序的性能和响应能力,本文主要介绍了C#实现异步操作的几种方式,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03

最新评论