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

Изменить : переместил актуальный вопрос в начало.

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

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

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

Далее следует следующая информация.

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

Возьмите этот class:

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); } } 

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


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

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

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

 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); } } 

Изменить: если вы просто имеете в виду сам делегат – я думаю, вы можете просто сделать:

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

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

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


Объединяя все это; вот пример, когда ни вызывающему, ни кодовому кодексу не нужно 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( (ac,o)=>calc.BeginAdd(x,y,ac,o), calc.EndAdd, result => { Console.WriteLine(result()); }); Console.ReadLine(); } } static class Async { public static void Run( Func begin, Func end, Action> callback) { begin(ar => { T result; try { result = end(ar); // ensure end called callback(() => result); } catch (Exception ex) { callback(() => { throw ex; }); } }, null); } } а 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( (ac,o)=>calc.BeginAdd(x,y,ac,o), calc.EndAdd, result => { Console.WriteLine(result()); }); Console.ReadLine(); } } static class Async { public static void Run( Func begin, Func end, Action> callback) { begin(ar => { T result; try { result = end(ar); // ensure end called callback(() => result); } catch (Exception ex) { callback(() => { throw ex; }); } }, null); } } 

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

Как только вы получите свой IAsyncResult:

 IAsyncResult ar; // this has your IAsyncResult from BeginInvoke in it MethodInvoker d = (MethodInvoker)ar.AsyncState; d.EndInvoke(ar);