Я до сих пор не получил делегатов - PullRequest
3 голосов
/ 30 декабря 2008

Разве использование делегатов не помогает в некоторых асинхронных случаях? Я попробовал следующее, но мой интерфейс все еще зависает. Когда на земле вы используете делегатов?

       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

Ответы [ 7 ]

7 голосов
/ 30 декабря 2008

Как упомянул Джоэл - BeginInvoke () выполнит делегат асинхронно - и вам нужно будет настроить обратный вызов Async для получения возвращаемого значения, если цель возвращает данные (используя EndInvoke).

Следующая ссылка - хорошая статья об использовании Делегатов для Асинхронного Программирования: http://msdn.microsoft.com/en-us/library/2e08f6yc.aspx

Также (это в C # - извините) вы можете обрабатывать обратный вызов, используя лямбда-выражения:

Action myAction = () => Console.WriteLine("This is an async call!");
myAction.BeginInvoke(asyncResult => Console.WriteLine("Async Done!"), null);
5 голосов
/ 30 декабря 2008

Это иронично, но любимый ответ каждого (используйте BeginInvoke) на самом деле не верен. Метод назначения делегата будет выполняться в потоке пула потоков. Вы не можете касаться элементов управления в потоке, отличном от потока, который их создал, почти всегда в основном потоке программы.

Если вы попробуете это с помощью .NET Framework версии 2.0 и выше в отладчике, цикл немедленно прекратится с IllegalOperationException. Чтобы исправить это, вы должны будете использовать Control.BeginInvoke (). Абсолютно другое животное от метода делегата BeginInvoke (), кстати.

Теперь вот ирония: ваш цикл теперь будет отправлять 10 000 запросов на вызов делегата в поток пользовательского интерфейса. На их выполнение уйдет несколько секунд, а не на выполнение какой-либо реальной работы. Как отправить событие Paint TextBox. Или ответить на любой пользовательский ввод. Вы на самом деле намного хуже, чем раньше.

Я сомневаюсь, что это очень помогает объяснениям делегатов. Может быть, вы можете выбрать лучший пример, который не пытается обновить элементы управления.

2 голосов
/ 30 декабря 2008

Лучшая аналогия, которую я нашел для объяснения делегатов, это завещание или ваша последняя воля.

Это набор инструкций, которые вы, конечно, пишете перед смертью и оставляете в безопасном месте. Затем, после вашей смерти, ваш адвокат выполнит эти инструкции ...

Делегаты используются в основном, когда код, который хочет выполнить действия, не знает достаточно подробностей того, каким должно быть это действие, вы можете рассматривать его как последовательность действий, которые должны быть выполнены в соответствующее время.

2 голосов
/ 30 декабря 2008

Звоните .BeginInvoke() вместо .Invoke().

2 голосов
/ 30 декабря 2008

Вот что я послал коллеге:

В основном делегаты могут использоваться для обратных вызовов, событий и асинхронной обработки.

Вот пример кода, скопируйте и вставьте каждый пример в новый проект командной строки.

//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();   
   }
1 голос
/ 30 декабря 2008

Как упоминают Джоэл и Мэтью, BeginInvoke выполнит делегат асинхронно. Как говорит Мэтью, эта статья MSDN хорошо описывает это.

Однако, если вы пытаетесь выполнить некоторую фоновую работу в приложении WinForms, я рекомендую вам проверить фоновый рабочий элемент управления . Это будет более знакомо разработчикам, привыкшим к управляемой событиями модели WinForms.

1 голос
/ 30 декабря 2008

В этом случае я могу не использовать делегата, так как вам нужно повторно делегировать обратно в поток пользовательского интерфейса для обновления кнопок. Вы можете использовать Application.DoEvents внутри цикла, чтобы адаптировать интерфейс при обновлении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...