.NET反射、事件、委托技术与设计模式

来源:互联网 发布:鹿鼎记 七个老婆 知乎 编辑:程序博客网 时间:2024/05/16 09:01
1 反射技术与设计模式 
        反射(Reflection)是.NET中的重要机制,通过放射,可以在运行时获得.NET中每一个类型(包括类、结构、委托、接口和枚举等)的成员,包括方法、属性、事件,以及构造函数等。还可以获得每个成员的名称、限定符和参数等。有了反射,即可对每一个类型了如指掌。如果获得了构造函数的信息,即可直接创建对象,即使这个对象的类型在编译时还不知道。

1.1 .NET可执行应用程序结构 
        程序代码在编译后生成可执行的应用,我们首先要了解这种可执行应用程序的结构。 
        应用程序结构分为应用程序域—程序集—模块—类型—成员几个层次,公共语言运行库加载器管理应用程序域,这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局。 
        程序集包含模块,而模块包含类型,类型又包含成员,反射则提供了封装程序集、模块和类型的对象。我们可以使用反射动态地创建类型的实例,将类型绑定到现有对象或从现有对象中获取类型,然后调用类型的方法或访问其字段和属性。反射通常具有以下用途。
(1)使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。
(2)使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。
(3)使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetConstructors或GetConstructor方法来调用特定的构造函数。
(4)使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法来调用特定的方法。
(5)使用FiedInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。
(6)使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。
(7)使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。
(8)使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。 
        System.Reflection.Emit命名空间的类提供了一种特殊形式的反射,可以在运行时构造类型。 
        反射也可用于创建称为类型浏览器的应用程序,使用户能够选择类型,然后查看有关选定类型的信息。 
        此外,Jscript等语言编译器使用反射来构造符号表。System.Runtime.Serialization命名空间中的类使用反射来访问数据并确定要永久保存的字段,System.Runtime.Remoting命名空间中的类通过序列化来间接地使用反射。

1.2 反射技术示例 
        下面是反射技术的示例,我们可以在程序去得时动态实例化对象,获得对象的属性,并调用对象的方法。
 1Namespace ReflectionExample
 2{
 3  class Class1
 4  {
 5      [STAThread]
 6      static void Main (string [ ] args)
 7      {
 8          System.Console.WriteLine(“列出程序集中的所有类型”);
 9          Assembly a = Assembly.LoadFrom (“ReflectionExample.exe”);
10          Type[ ] mytypes = a.GetTypes( );
11
12          Foreach (Type t in mytypes)
13          {
14              System.Console.WriteLine ( t.Name );
15          }

16          System.Console.ReadLine ( );
17      System.Console.WriteLine (“列出HellWord中的所有方法” );
18      Type ht = typeof(HelloWorld);
19      MethodInfo[] mif = ht.GetMethods();
20      foreach(MethodInfo mf in mif)
21      {
22          System.Console.WriteLine(mf.Name);
23      }

24      System.Console.ReadLine();
25      System.Console.WriteLine("实例化HelloWorld,并调用SayHello方法");
26      Object obj = Activator.CreateInstance(ht);
27      string[] s = {"zhenlei"};
28      Object bojName = Activator.CreateInstance(ht,s);
29      BindingFlags flags = (BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Static|BindingFlags.Instance|BindingFlags.DeclaredOnly);
30      MethodInfo msayhello = ht.GetMethod("SayHello");
31      msayhello.Invoke(obj,null);
32      msayhello.Invoke(objName,null);
33      System.Console.ReadLine();
34    }

35    }

36}

 1using System;
 2namespace ReflectionExample
 3{
 4    public class HelloWorld
 5    {
 6        string myName = null;
 7        public HelloWorld(string name)
 8        {
 9            myName = name;
10        }

11        public HelloWorld() : this(null)
12        {}
13        public string Name
14        {
15            get
16            {
17                return myName;
18            }

19        }

20        public void SayHello()
21        {
22            if(myName == null)
23            {
24                System.Console.WriteLine("Hello World");
25            }

26            else
27            {
28                System.Console.WriteLine("Hello," + myName);
29            }

30        }

31    }

32}

33

1.3 在设计模式实现中使用反射技术 
        采用反射技术可以简化工厂的实现。
(1)工厂方法:通过反射可以将需要实现的子类名称传递给工厂方法,这样无须在子类中实现类的实例化。
(2)抽象工厂:使用反射可以减少抽象工厂的子类。 
        采用反射技术可以简化工厂代码的复杂程度,在.NET项目中,采用反射技术的工厂已经基本代替了工厂方法。 
        采用反射技术可以极大地简化对象的生成,对以下设计模式的实现也有很大影响。
(1)命令模式:可以采用命令的类型名称作为参数直接获得命令的实例,并且可以动态执行命令。
(2)享元模式:采用反射技术实例化享元可以简化享元工厂。

2 委托技术与设计模式
        委托技术是.NET引入的一种重要技术,使用委托可以实现对象行为的动态绑定,从而提高设计的灵活性。

2.1 .NET中的委托技术
        .NET运行库支持称为“委托”的引用类型,其作用类似于C++中的函数指针。与函数指针不同,委托实例独立于其封装方法的类,主要是那些方法与委托类型兼容。另外,函数指针只能引用静态函数,而委托可以引用静态和实例方法。委托主要用于.NET Framework中的事件处理程序和回调函数。
        所有委托都从System.Delegate继承而来并且有一个调用列表,这是在调用委托时所执行方法的一个链接列表。产生的委托可以用匹配的签名引用任何方法,没有为具有返回类型并在调用列表中包含多个方法的委托定义返回值。
        可以使用的委托Cimbine及Remove方法在其调用列表中添加和移除方法。若要调用委托,可使用Invoke方法,或者使用BeginInvoke和EndInvoke方法异步调用委托。委托类的实现由运行库提供,而不由用户代码提供。
        委托适用于那种在某些语言中需要用函数指针来解决的情况,但是与函数指针不同,它是面向对象和类型安全的。
        委托声明定义一个类,它是从System.Delegate类派生的类。委托实例封装了一个调用列表,其中列出了一个或多个方法,每个方法称为一个可调用实体。对于实例方法,可调用实体由一个实例和该实例的方法组成;对于静态方法,可调用实体仅由一个方法组成。如果用一组合适的参数来调用一个委托实例,则该委托实例所封装的每个可调用实体都会被调用,并且使用上述同一组参数。
        委托实例的一个有用的属性是它既不知道,也不关心其封装方法所属类的详细信息,对它来说最重要的是这些方法与该委托的类型兼容。即只要方法的返回类型和参数表是相同的,则方法与委托类型兼容,方法的名称不一定要与委托类相同。
定义和使用委托分为声明、实例化和调用3个步骤。委托用委托声明语法声明,如:
    delegate void myDelegate( );
声明一个名为myDelegate的委托,它不带参数并且不返回任何结果,如:
class Test
{
       static void F( ) 
      {
              System.Console.WriteLine (“Test.F”);
      }
       static void Main ( )  
      {
            myeDelegate d = new myDelegate (F);
            d ( );
      }
}
创建一个myDelegate实例,然后立即调用它。这样做并没有太大的意义,因为直接调用方法会更简单。当涉及其匿名特性时,委托才能真正显示出其效果,如:
    void MultiCall (myDelegate d, int count )  {
      for  (int I = 0; I < count; I++)  {
        d( );
      }
    }
显示一个重复调用 myDelegate的MultiCall 方法,这个方法不知道,也不必知道myDelegate的目标方法的类型、该方法具有的可访问性或者是否为静态。对它来说最重要的是目标方法与myDelegate兼容。

2.2示例
        下面的例子说明了委托的实现,代码如下:
 1using System;
 2namespace DelegateExample
 3{
 4    public class TemplateMethod
 5    {
 6        public delegate float Comp(float a,float b);
 7        public Comp myComp;
 8        public TemplateMethod()
 9        {}
10        public float DoComp(float[] f)
11        {
12            float nf = float.NaN;
13            foreach(float df in f)
14            {
15                if(float.IsNaN(nf))
16                    nf = df;
17                else
18                    nf = myComp(nf,df);
19            }

20            return nf;
21        }

22
23    }

24}


2.3 委托技术与GOF设计模式中委托的关系
        需要指出的是,.NET中的委托技术与GOF在《设计模式》中所提列的委托的意图一致,但在实现方法上有相当大的区别。.NET中的委托更进一步地降低了对象间的耦合性,将静态的组合关系变为运行时的动态组合关系。
        GOF在《设计模式》中定义的委托是:“委托是一种组合方法,它使组合具有与继承同样的复用能力。在委托方式下,有两个对象参与处理一个请求,接受请求的对象将操作委托给它的代理者(delegate),它类似于子类将请求交给它的父类处理。使用继承时,被继承的操作总能引用接受请求的对象。在C++中通过this成员变量,在Smalltalk中则通过self。委托方式为了得到同样的效果,接受请求的对象将自身传给被委托者(代理人),使被委托的操作可以引用接受请求的对象。”
        如果采用.NET的委托技术,上述结构可以更加灵活。Window不引用Rectangle即可实现Area的计算,为此首先声明一个计算面积的委托定义,示例代码如下:
    public delegate float Darea();
然而在Window类中声明与这个代理一致的接口:
    class Window
    { 
        public Darea Area;
    }
这里不需要引用Rectangle类,只是在执行时动态绑定即可:
Rectangle rc = new Rectangle();
Window w = new Window();
w.Area = new Darea(rc.Area);
        这样当调用w的Area时,实际调用的是Reactangel的Area方法。从实现意图上看,.NET的委托更好地实现了GOF所阐述的意图,结构上也更为灵活。但这两种委托解决的不是一个层面的问题,GOF的委托强调的是一种策略,而.NET和委托技术则是具体实现。

2.4 委托技术与设计模式实现
        采用委托技术可以进一步实现用组合代替继承的思路,很多采用继承实现的关系可以采用委托实现。采用委托可以简化下列设计模式的使用。
(1)模板方法:这种方法采用继承实现具体方法,采用委托可以动态实现方法的组合。
(2)观察者:可以使用事件委托实现观察者与主题之间的通信。
(3)中介者:使用委托可以去除工件与中介者之间的耦合关系。
 

 

Delegate
delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类。与其它的类不同,delegate类能够拥有一个签名(signature),并且它只能持有与它的签名相匹配的方法的引用。它所实现的功能与C/C++中的函数指针十分相似。它允许你传递一个类A的方法m给另一个类B的对象,使得类B的对象能够调用这个方法m。但与函数指针相比,delegate有许多函数指针不具备的优点。首先,函数指针只能指向静态函数,而delegate既可以引用静态函数,又可以引用非静态成员函数。在引用非静态成员函数时,delegate不但保存了对此函数入口指针的引用,而且还保存了调用此函数的类实例的引用。其次,与函数指针相比,delegate是面向对象、类型安全、可靠的受控(managed)对象。也就是说,runtime能够保证delegate指向一个有效的方法,你无须担心delegate会指向无效地址或者越界地址。
实现一个delegate是很简单的,通过以下3个步骤即可实现一个delegate:
1.声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。
2. 创建delegate对象,并将你想要传递的函数作为参数传入。
3. 在要实现异步调用的地方,通过上一步创建的对象来调用方法。using System;

public class MyDelegateTest
{
        // 步骤1,声明delegate对象
        public delegate void MyDelegate(string name);

        // 这是我们欲传递的方法,它与MyDelegate具有相同的参数和返回值类型
        public static void MyDelegateFunc(string name)
        {
                  Console.WriteLine("Hello, ", name);
        }
        public static void Main()
        {
                  // 步骤2,创建delegate对象
 MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc);
                 // 步骤3,调用delegate
                 md("sam1111");
        }
}

输出结果是:Hello, sam1111

了解了delegate,下面我们来看看,在C#中对事件是如何处理的。

C#中的事件处理实际上是一种具有特殊签名的delegate,象下面这个样子:

public delegate void MyEventHandler(object sender, MyEventArgs e);

其中的两个参数,sender代表事件发送者,e是事件参数类。MyEventArgs类用来包含与事件相关的数据,所有的事件参数类都必须从System.EventArgs类派生。当然,如果你的事件不含参数,那么可以直接用System.EventArgs类作为参数。

就是这么简单,结合delegate的实现,我们可以将自定义事件的实现归结为以下几步:

1.定义delegate对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。
2.定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。
3.定义事件处理方法,它应当与delegate对象具有相同的参数和返回值类型。
4. 用event关键字定义事件对象,它同时也是一个delegate对象。
5.用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。
6.在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是OnEventName。
7. 在适当的地方调用事件触发方法触发事件。

下面是一个简单的例子:

using System;
public class EventTest
{
        // 步骤1,定义delegate对象
       public delegate void MyEventHandler(object sender, System.EventArgs e);
       // 步骤2省略
       public class MyEventCls
       {
                // 步骤3,定义事件处理方法,它与delegate对象具有相同的参数和返回值类// 型
                public  void MyEventFunc(object sender, System.EventArgs e)
                {
                           Console.WriteLine("My event is ok!");
                }
       }
       // 步骤4,用event关键字定义事件对象
      private event MyEventHandler myevent;
      private MyEventCls myecls;
      public EventTest()
      {
                myecls = new MyEventCls();
          :      // 步骤5,用+=操作符将事件添加到队列中
                this.myevent += new MyEventHandler(myecls.MyEventFunc);
      }
      // 步骤6,以调用delegate的方式写事件触发函数
     protected void OnMyEvent(System.EventArgs e)
      {
               if(myevent != null)
                       myevent(this, e);
      }
     public void RaiseEvent()
      {
               EventArgs e = new EventArgs();
      :         // 步骤7,触发事件
               OnMyEvent(e);
      }
      public static void Main()
      {
               EventTest et = new EventTest();
               Console.Write("Please input ''a'':");
               string s = Console.ReadLine();
               if(s == "a")
               {
                     et.RaiseEvent();
               }
               else
              {
                        Console.WriteLine("Error");
              }
      }
}

输出结果如下,红色为用户的输入:

Please input ‘a’: a
My event is ok
 

 

1.委派的实现过程。
首先来看一下委派,委派其实就是方法的传递,并不定义方法的实现。事件其实就是标准化了的委派,为了事件处理过程特制的、稍微专业化一点的组播委派(多点委派)。下面举一个例子,我觉得把委派的例子和事件的例子比较,会比较容易理解。
using System;
 
class Class1
{
       delegate int MathOp(int i1,int i2);
       static void Main(string[] args)
       {
               MathOp op1=new MathOp(Add);
               MathOp op2=new MathOp(Multiply);
               Console.WriteLine(op1(100,200));
              Console.WriteLine(op2(100,200));
               Console.ReadLine();
       }
       public static int Add(int i1,int i2)
       {
               return i1+i2;
       }
       public static int Multiply(int i1,int i2)
       {
              return i1*i2;
       }
}
 
首先代码定义了一个委托MathOp,其签名匹配与两个函数Add()和Multiply()的签名(也就是其带的参数类型数量相同):
delegate int MathOp(int i1,int i2);
Main()中代码首先使用新的委托类型声明一个变量,并且初始化委托变量.注意,声明时的参数只要使用委托传递的函数的函数名,而不加括号:
MathOp op1=new MathOp(Add);
(或为MathOp op1=new MathOp(Multiply);)
委托传递的函数的函数体:
public static int Add(int i1,int i2)
{
       return i1+i2;
}
public static int Multiply(int i1,int i2)
{
      return i1*i2;
}
然后把委托变量看作是一个函数名,将参数传递给函数。 Console.WriteLine(op1(100,200));
Console.WriteLine(op2(100,200));
 
 
2.事件的实现过程
using System;
 
class Class1
{
       static void Main(string[] args)
       {
               Student s1=new Student();
               Student s2=new Student();
               s1.RegisterOK +=new Student.DelegateRegisterOkEvent(Student_RegisterOK);
               s2.RegisterOK +=new Student.DelegateRegisterOkEvent(Student_RegisterOK);
               s1.Register();
               s2.Register();
               Console.ReadLine();
       }
       static void Student_RegisterOK()
       {
              Console.WriteLine("Hello");
       }
}
 
class Student
{
       public delegate void DelegateRegisterOkEvent();
       public event DelegateRegisterOkEvent RegisterOK;
       public string Name;
       public void Register()
       {
               Console.WriteLine("Register Method");
              RegisterOK();
       }
}
在Student类中,先声明了委托DelegateRegisterOkEvent(),然后使用event和要使用的委托类型(前面定义的DelegateRegisterOkEvent委托类型)声明事件RegisterOK(可以看作是委托的一个实例。):
public delegate void DelegateRegisterOkEvent();
public event DelegateRegisterOkEvent RegisterOK;
然后在Main()函数中,实例化Student类,然后s1.RegisterOK事件委托给了Student_RegisterOK 方法。通过“+=”(加等于)操作符非常容易地为.Net对象中的一个事件添加一个甚至多个响应方法;还可以通过非常简单的“-=”(减等于)操作符取消这些响应方法。
然后,当调用s1.Register()时,事件s1.RegisterOK发生。   

3.C#中预定义事件处理方式
    学习事件,我觉得最不好理解的就是C#中预定义了事件,使我才开始学习事件时一头雾水。在查了些资料后,终于弄明白了一些,如下:
EventArgs是包含事件数据的类的基类,用于传递事件的细节。
EventHandler是一个委托声明如下(其在.Net类库中如下声明的)
public delegate void EventHandler( object sender , EventArgs e ) 所以,所有形如: 
void 函娄名(object 参数名,EventArgs 参数名);
的函数都可以作为Control类的Click事件响应方法了。如下面所定义的一个事件响应方法:
private void button1_Click(object sender, System.EventArgs e)
参数object sender表示引发事件的对象,(其实这里传递的是对象的引用,如果是button1的click事件则sender就是button1)System.EventArgs e 代表事件的相应信息,如鼠标的x,y值等。
下面我们研究一下Button类看看其中的事件声明,以Click事件为例。
public event EventHandler Click;
这里定义了一个EventHandler类型的事件Click
private void button1_Click(object sender, System.EventArgs e)
         {
                   ...
             }
这是我们和button1_click事件所对应的方法。注意方法的参数符合委托中的签名(既参数列表)。那我们怎么把这个方法和事件联系起来呢,请看下面的代码。
this.button1.Click += new System.EventHandler(this.button1_Click); (其实button1.Click 为System.EventHandler委派的实例事件。与委派中委派实例委托给某一方法非常相似)
把this.button1_Click方法绑定到this.button1.Click事件。
 
 
4.事件的参数的使用。
using System;
 
class Class1
{
       static void Main()
       {
               Student s1=new Student();
               s1.Name ="Student1";
               Student s2=new Student();
               s2.Name ="Student2";
              s1.RegisterOK +=new Student.DelegateRegisterOkEvent(Student_RegisterOK);
               s2.RegisterOK +=new Student.DelegateRegisterOkEvent(Student_RegisterOK);
 
               //当Register方法一执行,触发RegisterOK事件
               //RegisterOK事件一触发,然后执行Student_RegisterOK方法
               s1.Register();  
              s2.Register();
              Console.ReadLine();
       }
       static void Student_RegisterOK(RegisterOkArgs e)
       {
              Console.WriteLine(e.EventInfo);
       }
}
 
class Student
{
       public delegate void DelegateRegisterOkEvent(RegisterOkArgs e);
       public event DelegateRegisterOkEvent RegisterOK;
       public string Name;
       public void Register()
       {
              Console.WriteLine("Register Method");
              RegisterOK(new RegisterOkArgs("Student Name: "+Name));
       }
}
class RegisterOkArgs:EventArgs
{
       public string EventInfo;
       public RegisterOkArgs(string eventInfo):base()
       {
              this.EventInfo =eventInfo;
       }
}     

 委托与事件

示例代码:

 private void button10_Click(object sender, EventArgs e)
        
{
            EventHandler en 
= new EventHandler(Form1_EventH);
            EventHandler er 
= new EventHandler(Form1_EventH);

            
this.EventH += en;
            
this.EventH += er;
            
this.EventH += er;
        
            er 
+= en + er;
        }

 

多播委托与事件的多次响应有极大的相似之处如图:

EventH是我定义的事件。从监视窗口可以看到EventH中有一个_invocationList这样一个成员。它是一个object数组大家一看就会明白它其实就是用来存储响应这个事件的委托数组。

委托,监视如图:

这时大家应该很惊讶,委托内部也有一个_invoationList来存储它里面的委托。从而来实现多播委托。

清晰了解事件

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

namespace ConsoleApplication5 

class Program 

static void Main(string[] args) 

Console.Write(
"Please input a number:"); 

//声明MainMethod类的一个实例 
MainMethod m = new MainMethod(); 

//把我们的方法挂到 m 的NumberAdded事件上 
//请注意这里使用了 MainMethod.EventDelegate 这样的方法 
//来声明这个事件委托,因为委托也是一种类。 
//在类中声明一个委托,就像声明了一个嵌套的类。 
m.NumberAdded += new MainMethod.EventDelegate(OutputResult); 

//转换用户输入,并调用Add方法相加 
m.Add(int.Parse(Console.ReadLine())); 

Console.ReadKey(
false); 
}
 

//这个是我们的事件处理函数。 
static void OutputResult(object sender, EventArgs e) 

Console.WriteLine(
"Some Number were added!"); 
}
 
}
 

public class MainMethod 

//首先声明一个委托 
//委托的实例还叫委托,但这里,我们声明的是一个委托类型 
//请注意是“类型”。 
public delegate void EventDelegate(object sender, EventArgs e); 

//然后声明一个事件 
//使用刚定义的委托类型 
public event EventDelegate NumberAdded; 

public int Result; 

public void Add(int x) 

Result 
= x + 55

//比较常用的方法,如果事件不为空,就调用。 
//其实是说委托EventDelegate是否指向一个方法 
//如果有,就不为空。 
if (NumberAdded != null

NumberAdded(
thisnew EventArgs()); 
}
 
}
 

}
 
}
 
 从这段代码可以看出,在这里事件(NumberAdded)被声明为一个类型(MainMethod)的成员,当发生了一些事(有数被相加了),对象(这个类的实例m)就调用这个委托。如果我们写好一个方法(OutputResult),然后传递给这个委托(m.NumberAdded += new MainMethod.EventDelegate(OutputResult);)那么它就会做出我们希望它做的事情(输出“Some Number were added!”消息)。

学习C#委托总结 

学过C了,对委托就比较有概念性的认识,C#中的委托其实类似于C中的函数指针,C中我们可以把函数进行指针化,但在C#中不允许这么操作,所以C#中委托是安全的,它并不是直接对内存进行指向,而是编成中间件代码,所以这一点C#比较好,我们应该把委托看成一个新的概念,类似于类,只不过是个特殊的类,它需要声明,然后构造的时候要有一个与声明的参数,返回值一样的方法作为参数进行构造即可.

public  delegate  void processdelegate();//定义一个委托,一般不用pubic

public void chuli()//定义委托的匹配签名(事件处理)
  {

Response.Write("aaaaaaaaaa");
  }
private void Button2_Click(object sender, System.EventArgs e)
  {
   processdelegate process;
   process=new processdelegate(chuli);
  //Response.Write("Result:{0}",process(param1,param2));//有返回值的一般这样执行
    process();//无返回值的一般这样执行
  }

精彩的委托示例

作者:TomMax (笑望人生)        出处:csdn community
有许多人问的,.Net中的委托以及事件处理。我拿简单的例子说明一下,是现实中的例子:

比如说一个公司(场景),你是老板,手下有两个员工,小张和小王。
你命令小王,如果小张玩游戏,则小王扣去小张500元钱。

这就是现实中的委托。

实际上,在写程序中,程序员就是老板,小张和小王就是两个对象。小张玩游戏是一个方法,小张还有一个游戏事件,他玩游戏激发这个事件。而小王就是事件处理对象,他负责把小张的钱扣除500。

所以,委托有如下几个要素:
1 激发事件的对象--就是小张
2 处理对象事件的对象--就是小王
3 定义委托,就是你让小王监视小张。

如果这三个要素都满足的话,则你就写出了一个完整事件的处理。

下面有个例子:在vs.net2003 C#控制台应用程序编辑运行成功:
using System;

namespace CSharpConsole
{
public class 场景
{
[STAThread]
public static void Main(string[] args)
{
Console.WriteLine("场景开始了....");
// 生成小王
小王 w = new 小王();
// 生成小账
小张 z = new 小张();

// 指定监视
z.PlayGame += new PlayGameHandler(w.扣钱);

// 开始玩游戏
z.玩游戏();

console.writeline("场景结束...");
Console.ReadLine();
}
}

 

// 负责扣钱的人
public class 小王
{
public 小王()
{
Console.WriteLine("生成小王...");
}

public void 扣钱(object sender,EventArgs e)
{
Console.WriteLine("小王:好小子,上班时间胆敢玩游戏...");
Console.WriteLine("小王:看看你小子有多少钱...");
小张 f = (小张)sender;
Console.WriteLine("小张的钱: " + f.钱.ToString());
Console.WriteLine("开始扣钱......");
System.Threading.Thread.Sleep(500);
f.钱 = f.钱 - 500;
Console.WriteLine("扣完了....现在小张还剩下:" + f.钱.ToString());
}
}

// 如果玩游戏,则引发事件
public class 小张
{
// 先定义一个事件,这个事件表示“小张”在玩游戏。
public event PlayGameHandler PlayGame;
// 保存小张钱的变量
private int m_Money;

public 小张()
{
Console.WriteLine("生成小张....");
m_Money = 1000; // 构造函数,初始化小张的钱。
}

public int 钱 // 此属性可以操作小张的钱。
{
get
{
return m_Money;
}
set
{
m_Money = value;
}
}

public void 玩游戏()
{
Console.WriteLine("小张开始玩游戏了.....");
Console.WriteLine("小张:CS好玩,哈哈哈! 我玩.....");
System.Threading.Thread.Sleep(500);
System.EventArgs e = new EventArgs();
OnPlayGame(e);
}

protected virtual void OnPlayGame(EventArgs e)
{
if(PlayGame != null)
{
PlayGame(this,e);
}
}
}

// 定义委托处理程序
public delegate void PlayGameHandler(object sender,System.EventArgs e);

}

C#委托,事件理解入门

   在学习C#中的委托和事件过程中,我读了许多文章来理解他们二者究竟是怎么一回事,以及如何使用他们,现在我将整个的理解过程陈述以下,我学到的每一方面,恐怕也是你们需要掌握的-)。
什么是委托?
    委托和事件这两个概念是完全配合的。委托仅仅是函数指针,那就是说,它能够引用函数,通过传递地址的机制完成。委托是一个类,当你对它实例化时,要提供一个引用函数,将其作为它构造函数的参数。
  每一个委托都有自己的签名,例如:Delegate int SomeDelegate(string s, bool b);是一个委托申明,在这里,提及的签名,就是说SomeDelegate 这个委托 string bool 类型的形参,返回一个int 类型。
上面提及的:当你对委托实例化时,要提供一个引用函数,将其作为它构造函数的参数。这里要注意了:被引用的这个函数必须和委托有相同的签名。
看下面的函数:
private int SomeFunction(string str, bool bln){...}
你可以把这个函数传给SomeDelegate的构造函数,因为他们有相似的签名(in other words,他们都有相同的形参类型和个数,并且返回相同的数据类型)。
   SomeDelegate sd = new SomeDelegate(SomeFunction);
 sd 引用了 SomeFunction,也就是说,SomeFunction已被sd所登记注册,如果你调用 sdSomeFunction 这个函数也会被调用,记住:我所说 SomeFunction的含义,后面,我们会用到它。
 现在,你应该知道如何使用委托了,让我们继续理解事件之旅……
事件的理解
 我们知道,在C#中:
l        按钮(Button)就是一个类,当我们单击它时,就触发一次click事件。
l        时钟(Timer)也是一个类,每过一毫秒,就触发一次tick事件。
让我们通过一个例子来学习,假定有这样的情节:
 现在有一个Counter的类,它有一个方法 CountTo(int countTo, int reachableNum),该方法表示:在指定的时间段内(0~~countTo),当到达指定的时间点reachableNum时,就触发一次NumberReached事件。
它还有一个事件:NumberReached,事件是委托类型的变量。意思是:如果给事件命名,用event关键字和要使用的委托类型申明它即可,如下所示:
public event NumberReachedEventHandler NumberReached;
 
在上面的申明中,NumberReachedEventHandle 仅是一个委托,更确切的表示应该是:NumberReachedDelegate。但是微软从不这样认为MouseDelegate或者PaintDelegate,,而是称谓:MouseEventHandler 或者 PaintEventHandler。所以
NumberReachedEventHandler NumberReachedDelegate听起来更方便一些,OK?好了,让我们继续,现在你知道了,在我们声明事件之前,需要象下面这样的形式来定义委托:
public delegate void NumberReachedEventHandler(object sender, NumberReachedEventArgs e);
现在声明的委托 NumberReachedEventHandle,它有一个void 返回值,和objectNumberReachedEventArgs两个形参。就像我们在第一节中强调的那样,当实例化委托时,作为实参传入的函数也必须拥有和委托同样的签名。
 在你的代码中,你是否用过PaintEventArgs 或者 MouseEventArgs来确定鼠标的移动位置?是否在触发Paint事件的对象中用过Graphics 属性?实际上,为用户提供数据的类都是继承于System.EventArgs类,就是我们常说的事件参数类,如果事件不提供参数,就不定义该类。在我们的例子中,我们通过下面的类提供预期的时间点。
public class NumberReachedEventArgs : EventArgs
{
    private int _reached;
    public NumberReachedEventArgs(int num)
    {
        this._reached = num;
    }
    public int ReachedNumber
    {
        get
        {
            return _reached;
        }
    }
}
好,有了前面的介绍,让我们到Counter类里面看看:
namespace Events
{
    public delegate void NumberReachedEventHandler(object sender,
        NumberReachedEventArgs e);
 
    /// <summary>
    /// Summary description for Counter.
    /// </summary>
    public class Counter
    {
        public event NumberReachedEventHandler NumberReached;
       
        public Counter()
        {
            //
            // TODO: Add constructor logic here
            //
        }
        public void CountTo(int countTo, int reachableNum)
        {
            if(countTo < reachableNum)
                throw new ArgumentException(
                    "reachableNum should be less than countTo");
            for(int ctr=0;ctr<=countTo;ctr++)
            {
                if(ctr == reachableNum)
                {
                    NumberReachedEventArgs e = new NumberReachedEventArgs(
                        reachableNum);
                    OnNumberReached(e);
                    return;//don't count any more
                }
            }
        }
 
        protected virtual void OnNumberReached(NumberReachedEventArgs e)
        {
            if(NumberReached != null)
            {
                NumberReached(this, e);//Raise the event
            }
        }
}
Counter中,如果到达指定的时间点,就触发一次事件,有以下几个方面需要注意:
l        通过调用NumberReached(它是NumberReachedEventHandler委托的实例)来完成一次触发事件。
NumberReached(this, e); 通过这种方式,可以调用所有的注册函数。
l        通过 NumberReachedEventArgs e = new NumberReachedEventArgs(reachableNum); 为所有的注册函数提供事件数据。
l        看了上面的代码,你可能要问了:为什么我们直接用 OnNumberReached(NumberReachedEventArgs e)方法来调用NumberReachedthise),而不用下面的代码呢?
    if(ctr == reachableNum)
{
    NumberReachedEventArgs e = new NumberReachedEventArgs(reachableNum);
    //OnNumberReached(e);
    if(NumberReached != null)
    {
        NumberReached(this, e);//Raise the event
    }
    return;//don't count any more
}
这个问题问得很好,那就让我们再看一下OnNumberReached 签名:
protected virtual void OnNumberReached(NumberReachedEventArgs e)
你也明白关键字protected限定了只有从该类继承的类才能调用该类中的所有方法。
关键字 virtual 表明了在继承类中可以重写该方法。
这两点非常有用,假设你在写一个从Counter继承而来的类,通过重写OnNumberReached 方法,你可以在事件触发之前,进行一次其他的工作。
 
protected override void OnNumberReached(NumberReachedEventArgs e)
{
    //Do additional work
    base.OnNumberReached(e);
}
注意:如果你没有调用base.OnNumberReached(e), 那么从不会触发这个事件!在你继承该类而想剔出它的一些其他事件时,使用该方式是非常有用的。
l        还要注意到:委托 NumberReachedEventHandler 是在类定义的外部,命名空间内定义的,对所有类来说是可见的。
好,该我们来实际操作使用Counter类了。
 
在我们简单的应用程序中,我们有两个文本框,分别是:txtCountTotxtReachable

 下面是
btnRunclick事件:
private void btnRun_Click(object sender, System.EventArgs e)
       {
           if(txtCountTo.Text == "" || txtReachable.Text=="")
              return;
           oCounter.CountTo(Convert.ToInt32(txtCountTo.Text), Convert.ToInt32(txtReachable.Text));
       }
 
private void oCounter_NumberReached(object sender, NumberReachedEventArgs e)
       {
           MessageBox.Show("Reached: " + e.ReachedNumber.ToString());
   }
 
初始化事件处理的语法如下:
oCounter = new Counter();
          oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached);
         
现在你明白了你刚才所做的一切,仅仅初始化 NumberReachedEventHandler 委托类型的对象(就像你实例化其他对象一样),注意到 oCounter_NumberReached 方法的签名与我前面提到的相似。
还要注意我们用的是+= 而不是=;这是因为委托是特殊的对象,它可以引用多个对象(在这里是指它可以引用多个函数)。For example 如果有另外一个
和oCounter_NumberReached一样具有相同签名的函数oCounter_NumberReached2,这两个函数都可以被引用:
 
oCounter = new Counter();
           oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached);
           oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached2);
现在,触发一个事件后,上面两个函数被依次调用。
 
视情况而定,如果你想让oCounter_NumberReached2在NumberReached事件发生后不再被调用,可以简单地这样写:oCounter.NumberReached -= new NumberReachedEventHandler(oCounter_NumberReached2);
 
最后
 让我们看一下完整的源代码,以供参考:
 Form1.cs
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace Events
{
    
/// <summary>
    
/// Summary description for Form1.
    
/// </summary>

    public class Form1 : System.Windows.Forms.Form
    
{
        Counter oCounter 
= null;

        
private System.Windows.Forms.Button cmdRun;
        
private System.Windows.Forms.TextBox txtReachable;
        
private System.Windows.Forms.TextBox txtCountTo;
        
private System.Windows.Forms.Label label1;
        
private System.Windows.Forms.Label label2;
        
private System.Windows.Forms.Button btnRemoveDelegate;
        
/// <summary>
        
/// Required designer variable.
        
/// </summary>

        private System.ComponentModel.Container components = null;

        
public Form1()
        
{
            
//
            
// Required for Windows Form Designer support
            
//
            InitializeComponent();

            
//
            
// TODO: Add any constructor code after InitializeComponent call
            
//
            oCounter = new Counter();
            oCounter.NumberReached 
+= new NumberReachedEventHandler(oCounter_NumberReached);
            oCounter.NumberReached 
+= new NumberReachedEventHandler(oCounter_NumberReached2);
        }


        
/// <summary>
        
/// Clean up any resources being used.
        
/// </summary>

        protected override void Dispose( bool disposing )
        
{
            
if( disposing )
            
{
                
if (components != null
                
{
                    components.Dispose();
                }

            }

            
base.Dispose( disposing );
        }


        
Windows Form Designer generated code

        
/// <summary>
        
/// The main entry point for the application.
        
/// </summary>

        [STAThread]
        
static void Main() 
        
{
            Application.Run(
new Form1());
        }


        
private void btnRun_Click(object sender, System.EventArgs e)
        
{
            
if(txtCountTo.Text == "" || txtReachable.Text=="")
                
return;
            oCounter.CountTo(Convert.ToInt32(txtCountTo.Text), Convert.ToInt32(txtReachable.Text));
        }


        
private void oCounter_NumberReached(object sender, NumberReachedEventArgs e)
        
{
            MessageBox.Show(
"Reached: " + e.ReachedNumber.ToString());
        }

        
private void oCounter_NumberReached2(object sender, NumberReachedEventArgs e)
        
{
            MessageBox.Show(
"Reached2: " + e.ReachedNumber.ToString());
        }


        
private void btnRemoveDelegate_Click(object sender, System.EventArgs e)
        
{
            oCounter.NumberReached 
-= new NumberReachedEventHandler(oCounter_NumberReached2);
            oCounter.CountTo(Convert.ToInt32(txtCountTo.Text), Convert.ToInt32(txtReachable.Text));
        }

    }

}
 
 Counter.cs
 
using System;

namespace Events
{
    
public delegate void NumberReachedEventHandler(object sender, NumberReachedEventArgs e);

    
/// <summary>
    
/// Summary description for Counter.
    
/// </summary>

    public class Counter
    
{
        
public event NumberReachedEventHandler NumberReached;
        
        
public Counter()
        
{
            
//
            
// TODO: Add constructor logic here
            
//
        }

        
public void CountTo(int countTo, int reachableNum)
        
{
            
if(countTo < reachableNum)
                
throw new ArgumentException("reachableNum should be less than countTo");
            
for(int ctr=0;ctr<=countTo;ctr++)
            
{
                
if(ctr == reachableNum)
                
{
                    NumberReachedEventArgs e 
= new NumberReachedEventArgs(reachableNum);
                    OnNumberReached(e);
                    
return;//don't count any more
                }

            }

        }


        
protected virtual void OnNumberReached(NumberReachedEventArgs e)
        
{
            
if(NumberReached!=null)
            
{
                NumberReached(
this, e);
            }

        }

    }


    
public class NumberReachedEventArgs : EventArgs
    
{
        
private int _reached;
        
public NumberReachedEventArgs(int num)
        
{
            
this._reached = num;
        }

        
public int ReachedNumber
        
{
            
get
            
{
                
return _reached;
            }

        }

    }

}

C#中实现事件
      
事件就是发生的一件事情。把事件信息通知给其它对象的对象称为事件发布方(事件源)。对象可以把自己注册到事件中,该对象称为事件的订户(事件的注册用户),事件可以有一个或多个注册用户。事件只把事件信息通知给所注册的用户。
 
参考:
http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/csref/html/vclrfeventpg.asp
 
       C#中的事件需要借助于委托。它们使用委托调用预订了该事件的用户中的方法。当发布方引发事件时,可能会调用许多委托(视预订该事件的对象数量而定)。
       定义事件时,首先由发布方定义委托,然后才能定义事件。事件取决于委托。如:
       先定义一个委托delegateMe:
       Public delegate void delegateMe(object param);
       然后根据委托delegateMe定义事件eventMe:
       Public event delegateMe eventMe;
.net中事件的原形如下:
[attributes] [modifiers] event type declarator;
[attributes] [modifiers] event type member-name {accessor-declarations};
其中:
attributes(可选)
可选的声明信息。有关属性和属性类的更多信息,请参见 C# 属性
modifiers(可选)
可选的修饰符,包括:abstract , new, override, static, virtual, extern ,四个访问修饰符之一
type
希望与此事件关联的 delegate
declarator
事件名。
member-name
事件名。
accessor-declarations(可选)
访问器的声明,用于添加或移除客户代码中的事件处理程序。访问器函数为 addremove。只定义一个而不定义另一个是错误的。
 
为了创建并使用 C# 事件,必须采取以下步骤:
1.             创建或标识一个委托。如果正在定义自己的事件,还必须确保有与事件关键字一起使用的委托。如果已经预定义了事件(例如在 .NET Framework 中),则事件的使用者只需要知道委托的名称。
2.             创建一个类,包含:
a.                      从委托创建的事件。
b.                     (可选)验证用 event 关键字声明的委托实例是否存在的方法。否则,该逻辑必须放置在引发此事件的代码中。
c.                      调用此事件的方法。这些方法可以重写一些基类功能。
此类定义事件。
3.             定义一个或多个将方法连接到事件的类。所有这些类都包括:
·                         使用 += 运算符和 -= 运算符将一个或多个方法与基类中的事件关联。
·                         将与事件关联的方法的定义。
4.             使用此事件:
·                         创建包含事件声明的类对象。
·                         使用定义的构造函数,创建包含事件定义的类对象。
有关事件的更多信息,请参见MSDN
·                 10.7 事件
·                 事件教程
·                 事件和委托
 
示例:
using System;
 
namespace event_test
{
     //
     class DelegateEvent
     {
         public delegate void MyDelegate();
         public event MyDelegate NotifyEveryOne;
         // 定义一个可能引发事件的方法
         public void Notify()
         {
              if(NotifyEveryOne != null)
              {
                   System.Console.WriteLine("引发了事件!");
                  
                   //触发事件,与方法的使用方式相同。
                   NotifyEveryOne(); //事件通知委托对象,委托对象调用实例化时作为参数的方法.
              }
         }
     }
 
     class clsA
     {
         public void DispMethodA()
         {
              System.Console.WriteLine("已经把事件NotifyEveryOne通知到ClassA!");
         }
     }
 
     class clsB
     {
         public void DispMethodB()
         {
              System.Console.WriteLine("已经把事件NotifyEveryOne通知到ClassB!");
         }
     }
 
     class clsTest
     {
         ///<summary>
         ///应用程序的主入口点。
         ///</summary>
         [STAThread]
         static void Main(string[] args)
         {
              // 首先实例化一个事件源对象。
              DelegateEvent eventSource = new DelegateEvent();
             
              // 实例化事件的订户
              clsA objA = new clsA();
              clsB objB = new clsB();
 
              // 使用委托把对象及其方法注册到事件中
              eventSource.NotifyEveryOne +=new event_test.DelegateEvent.MyDelegate(objA.DispMethodA);
              eventSource.NotifyEveryOne +=new event_test.DelegateEvent.MyDelegate(objB.DispMethodB);
 
              eventSource.Notify();
         }
 
     }
}
 
       这样,总算完成了C#.net中委托与事件的学习,感觉掌握的难度不是很大,比较容易上手,使用起来比较方便。
 
16. .Net 事件模式 【程序设计: 猫叫一声,所有的老鼠都开始逃跑,主人被惊醒。(C#语言)
要求:1.要有联动性,老鼠和主人的行为是被动的。 2.考虑可扩展性,猫的叫声可能引起其他联动效应。】
解答:
//1、定义一个类型用于保存所有需要发送给事件通知接受者的附加信息(继承于System.EventArgs
public class CryArgs : EventArgs
        {
            public readonly string Name;
            public CryArgs(string name)
            {
                this.Name = name;
            }
        }
//2、定义一个委托类型,用于指定事件触发时被调用的方法类型
public delegate void SubEventHandler(object sender,CryArgs cryArgs);
//3、定义一个事件成员
public class Subject
    {
        public event SubEventHandler SubEvent;
        protected virtual void FireAway(CryArgs cryArgs)
        {
            if (this.SubEvent != null)
                this.SubEvent(this,cryArgs);
        }  
 
    }
    public class Cat : Subject
    {
        private string name;
        public Cat(string name)
        {
            this.name = name;
        }
        public void Cry()
        {
            CryArgs cryArgs = new CryArgs(name);
            //Console.WriteLine("cat cryed.");
           this.FireAway(cryArgs);
        }
    }
//4、定义一个受保护的虚方法,负责通知事件的登记对象
public abstract class Observer
    {
        public Observer(Subject sub)
        {
            sub.SubEvent += new SubEventHandler(Response);
        }
        public abstract void Response(object sender,CryArgs cryArgs);   
    }
 //5、定义一个方法,将输入转化为期望的事件
public class Mouse : Observer
    {
        private string name;
        public Mouse(string name, Subject sub) : base(sub)
        {  
            this.name = name;
        }
        public override void Response(object sender, CryArgs cryArgs)
        {
            Console.WriteLine(cryArgs.Name+"叫,"+name + " attempt to escape!");
        }
    }
    public class Master : Observer
    {
        public Master(Subject sub) : base(sub){}
        public override void Response(object sender, CryArgs cryArgs)
        {
            Console.WriteLine(cryArgs.Name + "叫," + "host waken");
        }
    }
//注册事件
static void Main(string[] args)
        {
            Cat c = new Cat("laomao");
            Mouse mouse = new Mouse("haozi", c);
            Master master = new Master(c);
            c.Cry();                      
        }
 

原创粉丝点击