2009-09-27 1 views
4

Редактировать: Перенесено актуальное вопрос на верх.Как я могу избежать передачи/хранения делегата при использовании BeginInvoke и EndInvoke?

Обновление: Нашел пример Microsoft, в конце которого был добавлен еще один код.

Мои вопросы таковы:

  1. Безопасно позвонить множественным BeginInvoke призывает тот же экземпляр делегата, или я должен построить новый экземпляр делегата для каждого вызова метода в полете?
  2. Если мне нужно построить новые экземпляры для каждого, есть ли способ получить исходный делегат из значения IAsyncResult?
  3. Есть ли какой-нибудь другой, лучший способ добавить асинхронную поддержку в мой класс, чем использование делегатов?

Подробнее см. Далее.

Я добавляю асинхронную поддержку моему классу и думал, что сделаю это просто.

Возьмите этот класс:

public class Calculator 
{ 
    public Int32 Add(Int32 a, Int32 b) 
    { 
     return a + b; 
    } 
} 

Я думал, что я мог бы просто сделать это:

public class Calculator 
{ 
    private delegate Int32 AddDelegate(Int32 a, Int32 b); 
    public Int32 Add(Int32 a, Int32 b) 
    { 
     return a + b; 
    } 

    public IAsyncResult BeginAdd(Int32 a, Int32 b, 
     AsyncCallback callback, Object obj) 
    { 
     return new AddDelegate(Add).BeginInvoke(a, b, callback, obj); 
    } 

    public Int32 EndAdd(IAsyncResult ar) 
    { 
     return new AddDelegate(Add).EndInvoke(ar); 
    } 
} 

Это не работает, так как эти два метода каждый построить свой собственный объект делегата и им. Проверка вызовов EndInvoke, чтобы проверить, является ли экземпляр делегата, который я вызываю, тот же, что и тот, который я изначально называл BeginInvoke.

Самый простой способ справиться с этим было бы просто сохранить ссылку на переменную, например:

public class Calculator 
{ 
    private delegate Int32 AddDelegate(Int32 a, Int32 b); 
    private AddDelegate _Add; 

    public Calculator() 
    { 
     _Add = new AddDelegate(Add); 
    } 

    public Int32 Add(Int32 a, Int32 b) 
    { 
     return a + b; 
    } 

    public IAsyncResult BeginAdd(Int32 a, Int32 b, 
     AsyncCallback callback, Object obj) 
    { 
     return _Add.BeginInvoke(a, b, callback, obj); 
    } 

    public Int32 EndAdd(IAsyncResult ar) 
    { 
     return _Add.EndInvoke(ar); 
    } 
} 

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


Update: Я нашел этот пример здесь Microsoft на Asynchronous Delegates Programming Sample. Он показывает, что ссылка на IAsyncResult ссылается на объект AsyncResult, а затем я могу получить исходный экземпляр делегата через свойство AsyncDelegate.

Это безопасный подход?

Другими словами, это класс класса?

public class Calculator 
{ 
    private delegate Int32 AddDelegate(Int32 a, Int32 b); 

    public Int32 Add(Int32 a, Int32 b) 
    { 
     return a + b; 
    } 

    public IAsyncResult BeginAdd(Int32 a, Int32 b, AsyncCallback callback, Object obj) 
    { 
     return new AddDelegate(Add).BeginInvoke(a, b, callback, obj); 
    } 

    public Int32 EndAdd(IAsyncResult ar) 
    { 
     AddDelegate del = (AddDelegate)((AsyncResult)ar).AsyncDelegate; 
     return del.EndInvoke(ar); 
    } 
} 

ответ

4

Edit: если вы только имеете в виду самого делегата - я думаю, что вы можете просто сделать:

public Int32 EndAdd(IAsyncResult ar) 
{ 
    var d = (AddDelegate)((AsyncResult)ar).AsyncDelegate; 
    return d.EndInvoke(ar); 
} 

Вы всегда могли захватить его в делегат; что-то вроде: Async without the Pain, которое позволяет вам использовать обратный вызов Action (или Action<T>).

Другие распространенные шаблоны включают события для обратного вызова и, возможно, ThreadPool.QueueUserWorkItem; намного проще, чем IAsyncResult.


Складывая все это вместе; вот пример, когда ни абонент, ни код должен подчеркнуты о IAsyncResult:

using System; 
using System.Runtime.Remoting.Messaging; 
public class Calculator 
{ 
    private delegate Int32 AddDelegate(Int32 a, Int32 b); 
    public Int32 Add(Int32 a, Int32 b) 
    { 
     return a + b; 
    } 

    public IAsyncResult BeginAdd(Int32 a, Int32 b, 
     AsyncCallback callback, Object obj) 
    { 
     return new AddDelegate(Add).BeginInvoke(a, b, callback, obj); 
    } 

    public Int32 EndAdd(IAsyncResult ar) 
    { 
     var d = (AddDelegate)((AsyncResult)ar).AsyncDelegate; 
     return d.EndInvoke(ar); 
    } 
} 
static class Program 
{ 
    static void Main() 
    { 
     Calculator calc = new Calculator(); 
     int x = 1, y = 2; 
     Async.Run<int>(
      (ac,o)=>calc.BeginAdd(x,y,ac,o), 
      calc.EndAdd, result => 
      { 
       Console.WriteLine(result()); 
      }); 
     Console.ReadLine(); 
    } 
} 
static class Async 
{ 
    public static void Run<T>(
    Func<AsyncCallback, object, IAsyncResult> begin, 
    Func<IAsyncResult, T> end, 
    Action<Func<T>> callback) 
    { 
     begin(ar => 
     { 
      T result; 
      try 
      { 
       result = end(ar); // ensure end called 
       callback(() => result); 
      } 
      catch (Exception ex) 
      { 
       callback(() => { throw ex; }); 
      } 
     }, null); 
    } 
} 
+0

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

+0

Хорошо, полагаю, я обнаружил, что пропустил через AsyncResult во время написания. Так это безопасно? Там не будет ситуации с такими делегатами, где возвращенный объект * не * объект AsyncResult? (обратите внимание, что я не спрашиваю об IAsyncResult в общем случае, просто от delegate.BeginInvoke.) –

+0

См. мой ответ для метода, который не зависит от конкретной реализации IAsyncResult – thecoop

2

Я всегда хранить экземпляр делегата я назвал его как часть AsyncState из в BeginInvoke, таким образом, вы всегда можете получить его без необходимости полагаться на конкретной реализации IAsyncResult

После того, как вы получили ваш IAsyncResult:

IAsyncResult ar; // this has your IAsyncResult from BeginInvoke in it 

MethodInvoker d = (MethodInvoker)ar.AsyncState; 
d.EndInvoke(ar); 
+0

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

+0

Вы можете сохранить объект [] в AsyncState, если вы хотите сохранить там другие вещи – thecoop

Смежные вопросы