3

使用委托不是帮助处理一些异步情况吗?我尝试了以下操作,但我的 UI 仍然挂起。你到底什么时候使用代表?

       Public Class Form1
    Private Delegate Sub testDelegate()

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As _
 System.EventArgs) Handles Button1.Click

        Dim d As testDelegate = New testDelegate(AddressOf Add)
        d.Invoke()

    End Sub

    Private Sub Add()
        For i As Integer = 0 To 10000
            TextBox1.Text = i + 1
        Next
    End Sub
End Class
4

7 回答 7

7

正如 Joel 所提到的 - BeginInvoke() 将异步执行委托 - 如果目标返回数据(使用 EndInvoke),您将需要设置一个异步回调来检索返回值。

以下链接是一篇关于使用委托进行异步编程的好文章:http: //msdn.microsoft.com/en-us/library/2e08f6yc.aspx

此外(这是在 C# 中 - 抱歉)您可以使用 lambda 表达式处理回调:

Action myAction = () => Console.WriteLine("This is an async call!");
myAction.BeginInvoke(asyncResult => Console.WriteLine("Async Done!"), null);
于 2008-12-30T00:46:15.027 回答
5

这有点讽刺,但每个人最喜欢的答案(使用 BeginInvoke)实际上并不正确。委托目标方法将在线程池线程上执行。除了创建它们的线程之外,您不得触摸其他线程上的控件,几乎总是程序的主线程。

如果您在调试器中使用 .NET 框架 2.0 及更高版本进行尝试,循环将立即以 IllegalOperationException 终止。要纠正这个问题,您必须使用 Control.BeginInvoke()。顺便说一句,与委托的 BeginInvoke() 方法完全不同的动物。

讽刺的是,您的循环现在将向 UI 线程发送 10,000 个委托调用请求。它将花费几秒钟来执行它们,而不是去做任何真正的工作。就像调度 TextBox 的 Paint 事件一样。或响应任何用户输入。你实际上比以前更糟了。

我怀疑这有助于解释代表。也许您可以选择一个更好的示例,即不尝试更新控件的示例。

于 2008-12-30T05:02:18.143 回答
2

这是我发送给同事的内容:

基本上委托可用于回调、事件和异步处理。

这是一些示例代码,将每个示例复制并粘贴到新的命令行项目中。

//Callbacks    
//-------------------------------------------------------------------------------------------------------     
delegate double DiscountRule(); // Declaration
delegate string MessageFormat(); // Declaration

class Message    
{
    public string Instance() { return "You save {0:C}"; }
    public static string Class() { return "You are buying for {0:C}"; }

    public void Out(MessageFormat format, double d)
    {
        System.Console.WriteLine(format(), d);

    }
}

class Discount
{
    public static double Apply(DiscountRule rule, double amount)
    {
        return rule() * amount; // Callback
    }

    public static double Maximum() { return 0.50; }
    public static double Average() { return 0.20; }
    public static double Minimum() { return 0.10; }
    public static double None() { return 0.00; }
}

class TestDelegate1
{
    public static void Main()    
    {   
        DiscountRule[] rules = { // Instantiations  
             Discount.None,
             Discount.Minimum,    
             Discount.Average,    
             Discount.Maximum,    
             };

        // Instantiation with a static method    
        MessageFormat format = Message.Class;

        double buy = 100.00;    
        Message msg = new Message();             
        msg.Out(format, buy); // Invocation     

        // Instantiation with an instance method    
        format = msg.Instance;

        foreach (DiscountRule r in rules)    
        {    
            double saving = Discount.Apply(r, buy); // Invocation    
            msg.Out(format, saving); // Invocation    
        }    
    }    
}

//--------------------------------------------------------------------------------------------------------    
//Events:     

//the delegate creates a new type    
delegate void UpdateEventHandler();

class Subject    
{    
    private int data;

    public int GetData() { return data; }   
    public void SetData(int value) { data = value; Changed(); }    
    public event UpdateEventHandler Changed;    
}

class Observer    
{    
    public Observer(Subject s) { subject = s; }    
    public Subject GetSubject() { return subject; }   
    private Subject subject;   
}

class HexObserver : Observer
{    
    public HexObserver(Subject s)    
        : base(s)   
    {    
        s.Changed += Update;   
    }

    public void Update()    
    {   
        System.Console.Write("0x{0:X} ", GetSubject().GetData());    
    }    
}

class DecObserver : Observer    
{
     public DecObserver(Subject s)    
        : base(s)    
    {    
        s.Changed += Update;   
    }

    public void Update()    
    {    
        System.Console.Write("{0} ", GetSubject().GetData());    
    }    
}

class TestEvent    
{    
    public static void Main()    
    {   
        Subject s = new Subject();

        //assigns a Hex observer to object s (the constructor does the += assignment)    
        HexObserver ho = new HexObserver(s);

        //assigns a Decimal observer to object s    
        DecObserver co = new DecObserver(s);     

        for (int c; ; )    
        {    
            System.Console.Write("\nEnter a character" +    
            "(followed by a return, ctrl-C to exit): ");

            c = System.Console.Read();    
            s.SetData(c);    
            System.Console.Read(); // Two reads to get rid of the \r\n on PC.    
            System.Console.Read();   
        }    
    }    
}

.

//--------------------------------------------------------------------------------------------------------  
//Asynchronous processing (from http://msdn.microsoft.com/en-us/library/h80ttd5f(VS.71).aspx)         
using System;    
using System.Runtime.Remoting.Messaging;

// Create an asynchronous delegate.    
public delegate bool FactorizingAsyncDelegate (    
         int factorizableNum,     
         ref int primefactor1,    
         ref int primefactor2);

// Create a class that factorizers the number.    
public class PrimeFactorizer    
{    
   public bool Factorize(
                int factorizableNum,      
                ref int primefactor1,    
                ref int primefactor2)   
   {    
      primefactor1 = 1;    
      primefactor2 = factorizableNum;

      // Factorize using a low-tech approach.    
      for (int i=2;i<factorizableNum;i++)   
      {
         if (0 == (factorizableNum % i))    
         {
            primefactor1 = i;    
            primefactor2 = factorizableNum / i;    
            break;    
         }    
      }

      if (1 == primefactor1 )    
         return false;    
      else    
         return true   ;    
   }    
}

// Class that receives a callback when the results are available.    
public class ProcessFactorizedNumber    
{    
   private int _ulNumber;

   public ProcessFactorizedNumber(int number)   
   {    
      _ulNumber = number;   
   }

   // Note that the qualifier is one-way.    
   [OneWayAttribute()]    
   public void FactorizedResults(IAsyncResult ar)    
   {
      int factor1=0, factor2=0; 

      // Extract the delegate from the AsyncResult.      
      FactorizingAsyncDelegate fd = (FactorizingAsyncDelegate)((AsyncResult)ar).AsyncDelegate;

      // Obtain the result.    
      fd.EndInvoke(ref factor1, ref factor2, ar);

      // Output the results.    
      Console.WriteLine("On CallBack: Factors of {0} : {1} {2}",     
                   _ulNumber, factor1, factor2);    
   }    
}

// Class that shows variations of using Asynchronous    
public class Simple    
{    
   // The following demonstrates the Asynchronous Pattern using a callback.    
   public void FactorizeNumber1()    
   {
      // The following is the client code.    
      PrimeFactorizer pf = new PrimeFactorizer();    
      FactorizingAsyncDelegate fd = new FactorizingAsyncDelegate (pf.Factorize);

      int factorizableNum = 1000589023, temp=0; 

      // Create an instance of the class that is going   
      // to be called when the call completes.   
      ProcessFactorizedNumber fc = new ProcessFactorizedNumber(factorizableNum);

      // Define the AsyncCallback delegate.    
      AsyncCallback cb = new AsyncCallback(fc.FactorizedResults);

      // You can use any object as the state object.    
      Object state = new Object();

      // Asynchronously invoke the Factorize method on pf.   
      IAsyncResult ar = fd.BeginInvoke(
                           factorizableNum,     
                           ref temp, 
                           ref temp,     
                           cb,     
                           state);          
      //    
      // Do some other useful work.    
      //. . .    
   }    

   // The following demonstrates the Asynchronous Pattern using a BeginInvoke, followed by waiting with a time-out.    
   public void FactorizeNumber2()    
   {    
      // The following is the client code.    
      PrimeFactorizer pf = new PrimeFactorizer();    
      FactorizingAsyncDelegate fd = new FactorizingAsyncDelegate (pf.Factorize);

      int factorizableNum = 1000589023, temp=0; 

      // Create an instance of the class that is going     
      // to be called when the call completes.
      ProcessFactorizedNumber fc = new ProcessFactorizedNumber(factorizableNum);

      // Define the AsyncCallback delegate.    
      AsyncCallback cb =     
           new AsyncCallback(fc.FactorizedResults);

      // You can use any object as the state object.    
      Object state = new Object();

      // Asynchronously invoke the Factorize method on pf.   
      IAsyncResult ar = fd.BeginInvoke(   
                        factorizableNum,     
                        ref temp,     
                        ref temp,     
                        null,    
                        null); 

      ar.AsyncWaitHandle.WaitOne(10000, false);

      if (ar.IsCompleted)  
      {   
         int factor1=0, factor2=0;          

         // Obtain the result.    
         fd.EndInvoke(ref factor1, ref factor2, ar);    

         // Output the results.    
         Console.WriteLine("Sequential : Factors of {0} : {1} {2}", 
                       factorizableNum, factor1, factor2);   
      }    
   }

   public static void Main(String[] args)    
   {    
      Simple simple = new Simple();    
      simple.FactorizeNumber1();    
      simple.FactorizeNumber2();   
   }
于 2008-12-30T00:39:08.190 回答
2

调用.BeginInvoke()而不是.Invoke().

于 2008-12-30T00:41:36.080 回答
2

我找到的最好的类比来解释代表这是一份遗嘱或你的最后遗嘱。

当然,这是你在死前写下的一套指令,然后你把它放在一个安全的地方。然后在您死后,您的律师将执行这些指示...

当想要执行动作的代码不知道该动作应该是什么的足够详细信息时,主要使用委托,您可以将其视为在适当时间执行的一系列动作。

于 2008-12-30T01:03:05.630 回答
1

在这种情况下,我可能不会使用委托,因为您需要重新委托回 UI 线程来更新按钮。您可以在循环内使用 Application.DoEvents 来使您的 UI 在更新时响应。

于 2008-12-30T00:48:28.813 回答
1

正如 Joel 和 Matthew 所提到的,BeginInvoke 将异步执行委托。正如 Matthew 所说,这篇 MSDN 文章很好地涵盖了这一点。

但是,如果您尝试在 WinForms 应用程序中做一些后台工作,我建议您查看后台工作控件。习惯于 WinForms 事件驱动模型的开发人员会更熟悉它。

于 2008-12-30T01:20:51.977 回答