Можно ли скрыть механизм обратного вызова в одном методе? - PullRequest
2 голосов
/ 22 марта 2012

У меня есть такой интерфейс:

interface IAuthentication
{
    void AuthenticateAsync(string user, string pwhash);
    event EventHandler<AuthenticationResult> AuthenticationDone;
}

Это работает, вызывая событие, когда это сделано. Теперь я хотел бы обернуть этот механизм в один блокирующий метод, который возвращает результат аутентификации после того, как он сделан:

AuthenticationResult Authenticate(string user, string pwhash)
{
    var auth = GetIAuthenticator();
    // ... do something
    return <the authentication result from the even argument>;
}

Возможно ли это как-нибудь?

Ответы [ 3 ]

3 голосов
/ 22 марта 2012

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

private AuthenticationResult Authenticate(string user, string pwhash)
{            
    IAuthentication auth = GetIAuthenticator();
    AuthenticationResult result = null;
    AutoResetEvent waitHangle = new AutoResetEvent(false);

    auth.AuthenticationDone += (o, e) =>
        {
            result = e;
            waitHangle.Set();
        };

    auth.AuthenticateAsync(user, pwhash);
    waitHangle.WaitOne(); // or waitHangle.WaitOne(interval);
    return result;
}
2 голосов
/ 22 марта 2012

Поскольку вы используете .Net 4.0, вы можете воспользоваться библиотекой параллельных задач.

Вот очень простая программа, которая показывает, как использовать TaskCompletionSource:

public class Test
{
    public void Go()
    {
        ThreadPool.QueueUserWorkItem((z) => this.Imp());
    }

    private void Imp()
    {
        Console.WriteLine("Asynchronous operation in progress (1/2)...");
        Thread.Sleep(2000);
        Console.WriteLine("Asynchronous operation in progress (2/2)...");

        if (this.Done != null)
        {
            this.Done(this, EventArgs.Empty);
        }
    }

    public event EventHandler Done;
}

internal class Program
{
    private static void Main(string[] args)
    {
        Test test = new Test();

        TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(null);

        Console.WriteLine("Starting asynchronous operation");

        Task.Factory.StartNew(() =>
        {
            test.Done += (sender, e) => tcs.SetResult(null);
            test.Go();
        });

        // Blocking until completion of the async operation
        var tmp = tcs.Task.Result;

        Console.WriteLine("Asynchronous operation completed");

        Console.ReadKey();
    }
}

Результат:

Starting asynchronous operation
Asynchronous operation in progress (1/2)...
Asynchronous operation in progress (2/2)...
Asynchronous operation completed

Как видите, поток выполнения блокируется до тех пор, пока не прекратится асинхронная операция.

2 голосов
/ 22 марта 2012
private AuthenticationResult Authenticate(string user, string pwhash)
{
    bool isDone = false;
    AuthenticationResult results = null
    var auth = GetIAuthenticator(); 
    auth.AuthenticationDone += (o, e) => 
    {
        isDone = true;
        results = e;
    };

    auth.AuthenticateAsync(user, pwhash);

    long maxWaitTimeSeconds = 10;
    long thresholdMilliseconds = 100;
    int countToWait = maxWaitTimeSeconds * 1000 / thresholdMilliseconds;
    while (!isDone || countToWait-- > 0)
    {
       Thread.Sleep(thresholdMilliseconds);
    }

    if (countToWait == 0 && !isDone)
    {
       // TODO: timeout handling
    }

    return results;    
}

PS: Если аргументы событий никогда не могут быть нулевыми - вы можете избавиться от переменной isDone и просто использовать result != null в качестве индикатора "аутентификации выполнено"

...