C#基础(2)

6.委托

 

委托是一种引用类型,存储了对方法的引用,是一种类型安全的使用回调的方法。委托能够将方法作为实体赋值给变量和参数传递。委托不知道也不关心它所引用的方法的类,它仅仅关心所引用的方法与委托具有相同的参数和返回类型

和函数指针相比,主要有以下两点

1) 函数指针式不安全的类型。委托时完全面向对象和类型安全的。

2) 函数指针仅指向成员函数,而委托同时封装了对象实例和方法。

声明一个委托的唯一方法是使用关键字delegate,所有的委托实际上都是从System.MulicastDelegate类派生而来的,而MulicastDelegate又是从System.Delegate派生,当编译器处理下面委托声明的时候:

 

public delegate int PrintMessageHandler(Object target, Int32 methodPtr);

 

实际上将定义一个具有如下形式的类:

public class MyDelegate : System.MulicastDelegate
{
   
//构造函数
    public MyDelegate(Object target, Int32 methodPtr);
   
//声明与委托一致的方法
    public Int32 Invoke(Object target, Int32 methodPtr);
}

委托链

可以使用二元+和+=运算符来组合委托,使用一元-和-=运算符来从委托链中移除一个委托实例。

 

匿名方法

创建委托实例时必须明确指定使用的方法名称,这样比较麻烦。C#2.0引入了匿名方法(anmymous method),即允许与委托关联的代码以"内联"的方式写入使用委托的位置。使用匿名方法就可以将代码作为参数来创建委托的参数,而不用先定一个方法,再将方法名作为参数来创建委托

 

实例代码:

C#基础(2)C#基础(2)Code
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace WindowsFormsApplication1.DelegateAndEvent
{
    
// 定义一个委托
    public delegate int PrintMessageHandler(string message);
    
public class PrintProvider
    {
        
public int Print3(string msg)
        {
            Console.WriteLine(
"3======" + msg + "======");
            
return msg.Length;
        }
    }

    
class Delegate
    {
        
static int Print1(string msg)
        {
            Console.WriteLine(
"1======" + msg + "======");
            
return msg.Length;
        }

        
static int Print2(string msg)
        {
            Console.WriteLine(
"2======" + msg + "======");
            
return msg.Length+1;
        }

        
// 使用委托实例作为参数
        static void PrintTitle(PrintMessageHandler prn)
        {
            prn(
"==================================");
            prn(
"             XYZ 银行             ");
            prn(
"==================================");
        }

        
static void Main()
        {
            PrintProvider p
=new PrintProvider();
            
//使用实例方法创建委托实例
            PrintMessageHandler prn3 = new PrintMessageHandler(p.Print3);
            
// 使用静态方法来创建委托实例    
            PrintMessageHandler prn1 = new PrintMessageHandler(Delegate.Print1);
            PrintMessageHandler prn2 
= new PrintMessageHandler(Delegate.Print2);

            
string s1 = "直接调用委托实例1";
            
int n1 = prn1(s1); // 直接调用委托实例
            string s2 = "直接调用委托实例2";
            
int n2 = prn2(s2); // 直接调用委托实例
            string s3 = "直接调用委托实例3";
            
int n3 = prn3(s3); // 直接调用委托实例

            Console.WriteLine();

            Console.WriteLine(
"======将委托实例作为参数来传递=====");
            PrintTitle(prn1);  
            Console.WriteLine();
            Console.WriteLine(
"======使用匿名方法作为参数来传递=====");
            PrintMessageHandler anony 
= delegate(string msg)
            {
                Console.WriteLine(
"++++++" + msg + "++++++");
                
return 22;
            };
            PrintTitle(anony);

            Console.WriteLine();
            Console.WriteLine(
"========================委托链=======================");

            PrintMessageHandler composeHandler 
= prn1 + prn2;
            
int n4=composeHandler("委托链");
            Console.WriteLine(
"委托链的返回值就是最后一个方法的返回值:prn1+prn2======" + n4.ToString() + "======");
            Console.WriteLine();

            composeHandler 
-= prn2;
            n4 
= composeHandler("委托链");
            Console.WriteLine(
"委托链的返回值就是最后一个方法的返回值:-prn2======" + n4.ToString() + "======");

            Console.ReadLine();
        }
    }
}

执行结果

 

C#基础(2)

 

7.事件(以委托为基础而实现的一种类之间通信的方式

事件是类向外界发出的消息,以通知发生了某个特定的行为或者某个特定的条件已经成立

 

下面的为事件的声明:注意事件的类型必须是委托类型,以及event关键字

  // 定义delegate
  public delegate void TestEventHandler(object sender, TestEventArgs e);
  
// 用event关键字声明事件对象
  public event TestEventHandler TestEvent;

 

      使用事件

事件功能又三个互相关联的元素提供:

A.提供事件数据的类:该类必须从System.EventArgs继承

B.事件委托

C.引发事件的类:该类必须提供事件声明和引发事件的方法。

实例代码:

 

C#基础(2)C#基础(2)Code
//定义事件参数类
public class TestEventArgs:EventArgs
{
   
public readonly char KeyToRaiseEvent;
   
public TestEventArgs(char keyToRaiseEvent)
   {
      KeyToRaiseEvent 
= keyToRaiseEvent;
   }
}

// 发布事件的类
public class TestEventSource
{
    
// 定义delegate
    public delegate void TestEventHandler(object sender, TestEventArgs e);
    
// 用event关键字声明事件对象
    public event TestEventHandler TestEvent;

    
// 事件触发方法
    protected virtual void OnTestEvent(TestEventArgs e)
    {
        
if (TestEvent != null)
            TestEvent(
this, e);
    }

    
// 引发方法
    public void RaiseEvent(char keyToRaiseEvent)
    {
        TestEventArgs e 
= new TestEventArgs(keyToRaiseEvent);
        OnTestEvent(e);
    }
}

// 侦听事件的类
public class TestEventListener
{
    
// 定义处理事件的方法,它与声明事件的delegate具有相同的参数和返回值类型
    public void KeyPressed(object sender, TestEventArgs e)
    {
        Console.WriteLine(
"发送者为:{0}, 所按的键为:{1}", sender, e.KeyToRaiseEvent);
    }

    
// 订阅事件
    public void Subscribe(TestEventSource eventSource)
    {
        
//使用+=订阅事件
        
//委托必须是事件的委托类型一致
        eventSource.TestEvent += new TestEventSource.TestEventHandler(KeyPressed);
    }

    
// 取消订阅事件
    public void UnSubscribe(TestEventSource eventSource)
    {
        
//使用-=取消订阅事件
        eventSource.TestEvent -= new TestEventSource.TestEventHandler(KeyPressed);
    }
}

// 测试类
public class Test1
{
    
static void Main()
    {
        
// 创建事件源对象
        TestEventSource es = new TestEventSource();
        
// 创建侦听对象
        TestEventListener el = new TestEventListener();

        
// 订阅事件
        Console.WriteLine("订阅事件\n");
        el.Subscribe(es);

        
// 引发事件
        Console.Write("输入一个字符,再按Enter键:");
        
string s = Console.ReadLine();
        es.RaiseEvent(s.ToCharArray()[
0]);

        
// 取消订阅事件
        Console.WriteLine("\n取消订阅事件\n");
        el.UnSubscribe(es);
        
// 引发事件
        Console.Write("输入一个字符,再按Enter键:");
        s 
= Console.ReadLine();
        es.RaiseEvent(s.ToCharArray()[
0]);
    }
}

 

 

异步调用

异步操作通常用于执行完成时间可能较长的任务,如打开大文件、连接远程计算机或查询数据库。异步操作在主应用程序线程以外的线程中执行。应用程序调用方法异步执行某个操作时,应用程序可在异步方法执行其任务时继续执行。

.NET框架能够对任何方法进行异步调用。进行异步调用时,需要定义与异步调用的方法具有相同签名的委托。公共语言运行时会自动使用适当的签名为该委托定义BeginInvoke和EndInvoke方法

BeginInvoke以及EndInvoke

BeginInvoke方法用于启动异步调用,它与需要异步执行的方法具有相同的参数。另外,他还有两个参数。第一个参数是AsyncCallback委托,该委托引用在异步调用完成时要调用的方法。第二个参数是一个用户定义的对象,该对象可向回调方法传递数据。BeginInvoke立即返回,不会等待异步调用完成,被调用的方法将在线程池线程中执行。因此,提交请求的原始线程与执行异步方法的线程池线程是并行执行的。BeginInvoke会返回一个IAsyncResult对象,可以使用该对象来监视异步调用进度,也可以将该对象传递给EndInvoke方法,以获取异步执行方法的返回值。

EndInvoke方法用于检索异步调用的结果。调用BeginInvoke方法后可随时调用EndInvoke方法;如果异步调用尚未完成,EndInvoke方法将一直阻塞调用线程,直到异步调用完成以后才允许调用线程执行。EndInvoke方法的参数包括需要异步执行的outref参数,以及由BeginInvoke返回的IAsyncResult对象。因此,通过EndInvoke方法可以获得异步调用的方法的所有输出数据,包括返回值、out、和ref参数

 

用户界面与多线程

在相应界面操作时,如果操作时间太长,就会冻结界面。确保执行一项长响应任务时用户界面依然能够保持响应性的一种方法是:在一个后台线程中执行该任务。这不会使实际任务的运行速度更快,但是,当需要长时间运行的任务在后台运行时,应用陈旭的其他部分将同时并行进行,这样就能保证应用程序具有良好的响应性。

但是,窗体和空间都被绑定到特定的线程,不具备线程安全性。因此,如果窗体或控件在一个线程中创建,而在另一个线程中调用窗体或者空间的方法,就可能会产生不正确的结果

为了解决这个问题,System.Windows.Forms.Control类提供了几个线程安全的方法和属性,包括InvokeRequired属性、Invoke方法、BeginInvoke方法、EndInvoke方法和CreateGraphics方法。

Invoke方法用于在任何线程中安全地同步调用窗体或控件的任何方法;BeginInvoke方法用于在任何线程中安全地异步调用窗体或控件的任何方法,EndInvoke方法用于获取用BeginInvoke方法异步调用的方法的结果(返回值以及out和ref参数);InvokeRequired属性可用于确定当前环境对于窗体或控件是否安全的,如果InvokeRequired属性为true,就不能直接调用窗体和控件的方法,而必须使用Invoke或BeginInvoke方法来进行调用

一般原则是,只要有可能,就应该在创建窗体或控件的线程上执行窗体或控件的各种方法了如果需要从另一线程调用窗体或控件的方法,则必须使用InvokeBeginInvoke方法来进行调用

Invoke方法的原型如下:

C#基础(2)

 

 

如果要使用Invoke方法来调用一个方法,则必须定义一个签名与被调用方法相同的委托,然后使用Invoke再来执行指定的委托,并通过委托来调用方法。在调用该方法的时候,如果委托表示的方法具有参数表,则可以在Invoke调用中指定参数表。如果委托有返回值,则Invoke的Object类型返回值中将包含正被调用的委托的返回值;如果该委托没有返回值,那么Invoke方法将返回null。典型的使用Invoke的方法如下:

 

C#基础(2)C#基础(2)Code
//定义Invoke调用的委托
private delegate void ButtonOp (bool flag);
//签名与委托相同的方法
public void buttonOpAsync(bool flag)
{
   button1.Enabled 
= flag;
   button2.Enabled 
= flag;
   button3.Enabled 
= flag;
}

//线程中调用控件的方法
public void ThreadFunction()
{
   
bool flag=true;
   
if (this.InvokeRequired)
   {
    ButtonOp buttonOp
= new ButtonOp (buttonOpAsync);
    Invoke(buttonOp, flag);
   }
   
else
   {
   buttonOpAsync(flag);
   }
}

 

转载于:https://www.cnblogs.com/bhsc881114/archive/2009/05/17/1458722.html