Два вопроса об шаблоне AsyncCallback и IAsyncResult

Два вопроса по шаблону обратного вызова с AsyncCallback и IAsyncResult.

Я изменил вопрос с примера кода:

using System;
using System.Collections.Generic;
using System.Text;

namespace TestAsync
{
    class Program
    {
        private static Wrapper test = new Wrapper();

        static void Main(string[] args)
        {
            test.BeginMethod("parameter 1", "parameter 2", Callback);
            Console.ReadKey();
        }

        private static void Callback(IAsyncResult ar)
        {
            string result = test.EndMethod(ar);
        }
    }

    public interface ITest
    {
        IAsyncResult BeginMethod(string s1, string s2, AsyncCallback cb, object state);
        string EndMethod(IAsyncResult result);
    }

    public class Wrapper
    {
        private ITest proxy = new Test();

        public void BeginMethod(string s1, string s2, AsyncCallback cb)
        {
            proxy.BeginMethod(s1, s2, cb, proxy);
        }

        public string EndMethod(IAsyncResult result)
        {
            return ((ITest)(result.AsyncState)).EndMethod(result);
        }
    }

    public class Test : ITest
    {
        private string WorkerFunction(string a, string b)
        {
            // "long running work"
            return a + "|" + b;
        }

        public IAsyncResult BeginMethod(string s1, string s2, AsyncCallback cb, object state)
        {
            Func<string, string, string> function = new Func<string, string, string>(WorkerFunction);
            IAsyncResult result = function.BeginInvoke(s1, s2, cb, state);
            return result;
        }

        public string EndMethod(IAsyncResult result)
        {
            return (string)(result.AsyncState);
        }
    }

    public delegate TResult Func<T1, T2, TResult>(T1 t1, T2 t2);
}

НАЧАТЬ ИЗОБРАЖЕНИЕ
Я начинаю понимать, что происходит. Я перепутал асинхронный шаблон WCF и обычный асинхронный шаблон. В WCF используется прокси, а Begin- и EndMethod должны быть переданы прокси, а не делегат функции. В случае WCF кастинг работает, в обычном случае нет. WCF использует атрибут [OperationContract (AsyncPattern = true)], вероятно, для обеспечения несколько другого шаблона. END EDIT

Почему ошибка на строке return (string)(result.AsyncState);?
Точно такой же шаблон в производственном коде одобрен.

Во-вторых, почему я не могу отлаживать код в BeginMethod класса Test?
Я могу только сломать WorkerFunction.

Ответ 1

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

public class Test
{
    private int WorkerFunction(string a, string b)
    {
        //this is the guy that is supposed to do the long running work 
        Console.WriteLine(a);
        Console.WriteLine(b);
        return a.Length + b.Length;
    }

    private void MyCallBack(IAsyncResult ar)
    {
        Func<string, string, int> function = ar.AsyncState as Func<string, string, int>;
        int result = function.EndInvoke(ar);
        Console.WriteLine("Result is {0}", result);
    }
    public void CallMethod()
    {
        Func<string, string, int> function = new Func<string, string, int>(WorkerFunction);
        IAsyncResult result = function.BeginInvoke("param1", "param2", MyCallBack, function);
    }


}

class Program
{

    static void Main(string[] args)
    {
        Test test = new Test();
        test.CallMethod();
    }
}

Как вы можете видеть, функция обратного вызова (MyCallBack) получает объект IAsyncResult, переданный обратно. Именно этот объект IAsynchResult, AyncState которого дает вам исходный объект, который вы передали в вызове метода BeginInvoke. В этом случае (и как общая практика) вы передаете сам делегат как объект (который был переменной, называемой "функция" ). Один вызываемый вызов был вызван, после чего я получил исходный объект делегирования, запросив ar.AsyncState, затем я вызвал EndInvoke, чтобы вернуть результат.

Что касается того, что точка останова не попала, я боюсь, мне нужна дополнительная информация об этом. Что именно ты имеешь ввиду? Где этот оператор Console.WriteLine?

НОВЫЙ ОТВЕТ Хорошо, вот моя версия вашего кода. В принципе, независимо от того, где вы вызываете EndInvoke, вам нужно вызвать его на фактическом объекте делегирования (в вашем случае вы создаете переменную "function", передавая ей фактический объект IAsyncResult). Код, который у вас есть, пытается замаскировать это средство, но я должен сказать, что есть менее сложные способы сделать это. Я буду более чем счастлив написать вам упаковку, если вы пожелаете. На данный момент я просто даю вам свой код обратно с моим небольшим дополнением в нем, что должно заставить его работать. Поскольку вы используете переменные уровня класса, поэтому я вынужден использовать его самостоятельно. На данный момент это действительно не безопасно. Но здесь идет

using System;
using System.Collections.Generic;
using System.Text;

namespace TestAsync
{
    class Program
    {
        private static Wrapper test = new Wrapper();

        static void Main(string[] args)
        {
            var objectState = new object();
            test.BeginMethod("parameter 1", "parameter 2", Callback, objectState);
            Console.ReadKey();
        }

        private static void Callback(IAsyncResult ar)
        {
            string result = test.EndMethod(ar);
            Console.WriteLine(result);
        }
    }

    public interface ITest
    {
        IAsyncResult BeginMethod(string s1, string s2, AsyncCallback cb, object state);
        string EndMethod(IAsyncResult result);
    }

    public class Wrapper
    {
        private ITest proxy = new Test();

        public void BeginMethod(string s1, string s2, AsyncCallback cb)
        {
            proxy.BeginMethod(s1, s2, cb, proxy);
        }

        public string EndMethod(IAsyncResult result)
        {
            return ((ITest)(result.AsyncState)).EndMethod(result);
        }
    }

    public class Test : ITest
    {
        Func<string, string, string> _delgateObject;
        private string WorkerFunction(string a, string b)
        {
            // "long running work"
            return a + "|" + b;
        }

        public IAsyncResult BeginMethod(string s1, string s2, AsyncCallback cb, object state)
        {
            Func<string, string, string> function = new Func<string, string, string>(WorkerFunction);
            this._delgateObject = function;
            IAsyncResult result = function.BeginInvoke(s1, s2, cb, state);
            return result;
        }

        public string EndMethod(IAsyncResult result)
        {
            var test = result.AsyncState;
            return this._delgateObject.EndInvoke(result);
        }
    }

    public delegate TResult Func<T1, T2, TResult>(T1 t1, T2 t2);
}

Ответ 2

Эта статья помогла мне понять, что происходит. Wcf OperationContract реализует специальный шаблон Async, который синхронно вызывает [Operation] в отдельном потоке. Начать [Операция] и Закончить [Операция] используются для создания шаблона, но на самом деле они не будут задействованы. Таким образом, этот шаблон с его сигнатурами и атрибутами, похоже, идентичен выполнению синхронного вызова на клиенте посредством, например, BackgroundWorker.

Вы можете установить только атрибут AsyncPattern [из атрибута OperationContract] в метод с совместимой с BeginOperation сигнатурой, а определяющий контракт должен также иметь метод сопоставления с совместимой с EndOperation сигнатурой. Эти требования проверяются на время загрузки прокси. То, что делает AsyncPattern, связывает базовый синхронный метод с парой Begin/End и сопоставляет синхронное выполнение с асинхронным. Вкратце, когда клиент вызывает метод формы BeginOperation с AsyncPattern, установленным в true, он сообщает WCF не пытаться напрямую вызывать метод с помощью этого имени в службе. Вместо этого он будет использовать поток из пула потоков для синхронного вызова базового метода (идентифицированного именем Action). Синхронный вызов блокирует поток из пула потоков, а не вызывающего клиента. Клиент будет заблокирован только в тот момент, когда потребуется отправить запрос на вызов в пул потоков. Метод ответа синхронного вызова коррелирован с методом EndOperation.