Delegate比较全面的例子

将Delegate理解为接口,只有一个方法的接口,这样最容易理解。这个方法只有声明,没有实现,实现在别的类。(实际上应该把它看作函数指针,不过接口更容易理解些。)

在你的类中有一个Delegate就相当于有一个接口。通过这个接口你可以调用一个方法,而这个方法在别的类定义,由别的类来干。

为了说的形象一点,举个例子:

学生考试完后成绩出来了,考的好了老师要表扬,考的不好了老师要批评。

 

来源:http://www.cnblogs.com/idior/articles/100666.html

 

________________________________________________________________________________________________________________________

 

代表(delegate):

 它是C#语言里面的函数指针,代表可以指向某一个函数,在运行的时候调用这个函数的实现。下面来看看它的实现步骤:

  1. 声明一个delegate对象。
  2. 实现和delegate具有相同参数和返回值的函数实现(可以是静态和非静态的)。
  3. 产生一个delegate对象的时候,把你刚刚实现的函数作为参数传给他的构造函数。

请看下面例子:

using System;
using System.Collections.Generic;
using System.Text;

namespace UsingDelegate
{
    public delegate void MyDelegate(string mydelegate);//声明一个delegate对象

    public class TestClass
    {

        //实现有相同参数和返回值的函数
        public void HelloDelegate(string mydelegate)
        {
            Console.WriteLine(mydelegate);
        }

       //实现有相同参数和返回值的静态函数

        public static void HelloStaticDelegate(string mystaticdelegate)
        {
            Console.WriteLine(mystaticdelegate);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            TestClass testClass = new TestClass();
            MyDelegate mydelegate = new MyDelegate(testClass.HelloDelegate);//产生delegate对象
            mydelegate("Hello delegate");//调用

            MyDelegate myStaticDelegate = new MyDelegate(TestClass.HelloStaticDelegate);//产生delegate对象
            myStaticDelegate("Hello static delegate");//调用
        }
    }
}

 

事件(event):

让我通过一个例子来模拟事件的整个过程:

  1. 创建一个button类,它里面有一个click 事件。
  2. 创建一个Form类,他里面有一个我们上面定义的button类。
  3. 要求:当我们用户单击button类的时候From类要对他进行处理,输出一条信息“我知道你被单击了”

请看下图:

首先我们会单击button,然后button会通知Form,然后From就作出相应。这个过程在C#里面应该怎么做到呢?

下面我会列出上述例子的源代码(这里就不介绍怎么声明event等等内容了):

using System;
using System.Collections.Generic;
using System.Text;

namespace UsingEvent
{
    public delegate void ClickEventHandler(object sender, EventArgs e);//声明一个代表:请看文章最后面Note

    public class MyButton              //创建MyBottom
    {
        public event ClickEventHandler ClickEvent;//声明一个事件

        public void Click()                                 //单击MyButton
        {
            if (ClickEvent != null)
            {
                Console.WriteLine("MyButton: 我被单击了");
                ClickEvent(this, null);                          //抛出事件,给所有相应者
            }
        }
    }

    public class MyForm
    {
        public MyButton myButton = new MyButton();

        public MyForm()
        {

            //添加事件到myButton中,当myButton被单击的时候就会调用相应的处理函数

            myButton.ClickEvent += new ClickEventHandler(OnClickEvent);   

         }

       //事件处理函数

       void OnClickEvent(object sender, EventArgs e)
        {
            Console.WriteLine("MyForm: 我知道你被单击了!");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyForm form = new MyForm();//生成一个MyForm

            form.myButton.Click();//单击MyForm中的鼠标,效果就出来了
        }
    }
}
 

Note:public delegate void ClickEventHandler(object sender, EventArgs e);这是事件委托标准的声明方法,其实在参数里面我们可以不传,也可以是其他类型的。但是最好还是使用上面的声明方法,你可以继承EventArgs,来包装你要传送的其他任何参数。

 

______________________________________________________________________________________________________________________

 

先看一个简单的delegate的例子

        public static bool IsOdd(int i)
        {
            return (i & 1) == 1;
        }

        public delegate bool NumberTester(int i);

        public static void PrintMatchingNumbers(int from, int to, NumberTester filter)
        {
            for (int i = from; i <= to; ++i)
            {
                if (filter(i))
                {
                    Console.WriteLine(i);
                }
            }
        }
调用时只需要执行:

PrintMatchingNumbers(1, 10, new NumberTester(IsOdd));

很多情况下,我们只要执行只包含一个方法的代理。于是微软在.net 2.0里给我们提供了更方便的anonymous method,比如上面调用IsOdd的过程就能改写成下面这样:

             PrintMatchingNumbers(1, 10, delegate(int i)
                                        {
                                            return (i & 1) == 1;
                                         });

如此就避免了IsOdd的声明。微软提供匿名方法的道理是这样的:既然函数IsOdd只在一个地方被调用,那么直接在调用的地方实现岂不更省事儿。这样可以减少我们开发人员的输入。

接着到了3.0,微软更进了一步,他提出了lambda表达式。C#lambda表达式是这样定义的参数=> 表达式。于是,我们的代码变的更短:

              PrintMatchingNumbers(1, 10, i=>(i & 1) == 1);

也就是说lambda表达式作为一个函数并当作PrintMatchingNumbers的第三个参数来执行,而这个函数只要符合delegate NumberTester的签名就行。理所当然的lambda表达式可以附值给一个相同签名的delegate:
                NumberTester a =new NumberTester();
                a += i=>(i & 1) == 1;

C# 3.0里甚至可以把声明和初始化以及赋值合到一块儿,简写成:

        NumberTester a = i=>(i & 1) == 1;

如果我们把NumberTester声明为泛型的delegate,如下:

        public delegate U NumberTester<T, U>(T i);

那么这个泛型的delegate应用就太广泛了,涵盖了一切有一个输入参数和一个返回值的函数,所以直接命名为Func更合适,即:
        public delegate U Func<T, U>(T i);

其实上面这个类型正是在.net Framework 3.0 中最重要的命名空间 System.Core里定义的。
这样,就可以写成

Func<int,bool> a = i=>(i & 1) == 1;

下面我们再回过头来看看使用这个delegatePrintMatchingNumbers,应该作相应的更改

        public static void PrintMatchingNumbers(int from, int to, Func<int, bool> filter)
        {
            for (int i = from; i <= to; ++i)
            {
                if (filter(i))
                {
                    Console.WriteLine(i);
                }
            }
        }

当然,由于泛型的的引入,此处还可以进一步的把类型抽离出来以表达这个意思:在一个范围内的任意的类型的变量,以指定的方式递增,通过判断打印符合条件的值。也就可以得到算法的复用。

完整的例子:

        public delegate U Func<T, U>(T i);

        public delegate void Func<T>(ref T i);

        static void Main(string[] args)

        {

            DateTime from = DateTime.Parse("2004/06/01");

            DateTime to = DateTime.Parse("2005/01/18");

            PrintMatchingT<DateTime>(from, to,

                delegate(ref DateTime i) { i = i.AddDays(1); },

                delegate(DateTime i) { return (i.Month + i.Day == 11);});

            Console.WriteLine();

            Console.Read();

        }

        public static void PrintMatchingT<T>(T from, T to, Func<T> incre, Func<T, bool> filter) where T : IComparable<T>
        {
            for (T i = from; (i.CompareTo(to) < 0); incre(ref i))
            {
                if (filter(i))
                {
                    Console.WriteLine(i);
                }
            }
        }

这个例子是在指定的时间范围内以天为递增量,判断代表月与日的数字加起来为
11的日子。当然具体到这个例子,我们也能把bool改成泛型的,只要保证实际传入的类型能够有效的转化成bool类型的,不然filter(i)是没法放到 if判断中的。

由此可见:混合使用泛型和匿名代理是可以写出很精简的代码,当然这也往往意味着需要写更多的注释来说明你的意图。

本文起源于Ian Griffiths 的 C# 3.0 and LINQ - Expression Trees 

对 LINQ 和 Expression Trees 感兴趣的朋友,可以看看
http://www.cnblogs.com/sweatypalms/archive/2007/10/18/929312.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值