Conversione del metodo per restituire il tipo generico

1

Vorrei chiedere come convertire un metodo in modo che possa restituire qualsiasi tipo a seconda di cosa accade all'interno di esso.

Quello che ho è l'inizio di un'applicazione che visiterà un certo numero di pagine web (e alla fine eseguirà alcune attività su ciascuna) prima di passare alla successiva.

Al momento ho questo:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;

namespace WindowsFormsApplication12
{
public partial class Form1 : Form
{
    public Form1()
    {  
        InitializeComponent();
        webBrowser1.ScriptErrorsSuppressed = true;

        label1.MaximumSize = new Size(100, 0);
        label1.AutoSize = true;
    }

    private void go_Click(object sender, EventArgs e)
    {
        ProcessUrlsAsync(new[] { "http://google.com", "http://microsoft.com", "http://yahoo.com" }).Start();
    }

    private void Exit(object sender, EventArgs e)
    {
        Application.Exit();
    }


    private Task ProcessUrlsAsync(string[] urls)
    {
        return new Task(() =>
        {
            foreach (string url in urls)
            {
                TaskAwaiter<string> awaiter = ProcessUrlAsync(url);                  
                string result = awaiter.GetResult();

                label1.Invoke((MethodInvoker)(() => label1.Text = result));
            }
        });
    }

    private Task ProcessUrlsAsync(string url)
    {
        return new Task(() =>
        {
            TaskAwaiter<string> awaiter = ProcessUrlAsync(url);                    
            string result = awaiter.GetResult();

            MessageBox.Show(result);
        });
    }

    private TaskAwaiter<string> LoadWebPage(string url)
    {
        TaskCompletionSource<string> taskCompletionSource = new TaskCompletionSource<string>();
        var handler = new WebBrowserDocumentCompletedEventHandler((s, e) =>
        {
            if (e.Url.Equals(url))
            {
                taskCompletionSource.SetResult(e.Url.ToString());
            }
            else
            {
                taskCompletionSource.SetResult(e.Url + ": " + webBrowser1.Document.Title);
            }
        });
        webBrowser1.DocumentCompleted += handler;
        taskCompletionSource.Task.ContinueWith(s => { webBrowser1.DocumentCompleted -= handler; });

        webBrowser1.Navigate(url);
        return taskCompletionSource.Task.GetAwaiter();
    }
}
}

Quello che mi piacerebbe fare è cambiare

private TaskAwaiter<string> LoadWebPage(string url)

metodo in modo che restituisca un TaskAwaiter < tipo generico & gt ;. La mia ragione è che se non riesce ad accedere o si verifica un errore durante l'elaborazione, allora posso restituire un booleano o una stringa o un int per indicare il successo (o meno) delle azioni eseguite.

Inizialmente pensavo di sovraccaricare il metodo fino a quando non mi sono reso conto che questo non avrebbe funzionato con tipi di ritorno diversi, doh !!

I riferimenti alle pagine web sono solo lì come test per essere sicuro di poter avere le basi per aprire una pagina web. Una volta ottenuto questo, questi verranno sostituiti con le pagine Web dei miei sistemi prima che possa iniziare l'elaborazione.

È possibile convertire questo metodo in questo modo?

Modifica Mi piace l'aspetto della soluzione di @Bjarke Søgaard, quindi sto provando a implementarlo.

Sfortunatamente sono un po 'fitto a quest'ora della notte e per la vita di me non riesco a capire come usare il metodo. Finora ho questo:

TaskAwaiter<WebResponse> awaiter = LoadWebPage(url);


private TaskAwaiter<WebResponse> LoadWebPage(string url)
    {
        TaskCompletionSource<WebResponse> taskCompletionSource = new TaskCompletionSource<WebResponse>();
        var handler = new WebBrowserDocumentCompletedEventHandler((s, e) =>
        {
            //taskCompletionSource.SetResult(e.Url + ": " + webBrowser1.Document.Title);
            taskCompletionSource.SetResult(<< Stuck Here >>);
        });
        webBrowser1.DocumentCompleted += handler;
        taskCompletionSource.Task.ContinueWith(s => { webBrowser1.DocumentCompleted -= handler; });

        webBrowser1.Navigate(url);
        return taskCompletionSource.Task.GetAwaiter();
    }

Come puoi vedere, non so come impostare il risultato. Qualsiasi aiuto sarebbe apprezzato con questo.

Grazie

    
posta cosmarchy 18.02.2017 - 22:40
fonte

3 risposte

2

Che ne dici di restituire qualcosa del genere?

public class WebResponse<T>
{
  public T Result { get; private set; }

  public HttpStatusCode ResponseCode { get; private set; }

  public WebResponse(T result, HttpStatusCode code)
  {
    this.Result = result;
    this.ResponseCode = code;
  }

  // In case of error
  public WebResponse(HttpStatusCode code)
  {
    this.ResponseCode = code;
  }

  public bool IsSuccess()
  {
    switch (this.ResponseCode)
    {
      case HttpStatusCode.Success:
      case HttpStatusCode.SuccessEmpty:
      case HttpStatusCode.Accepted:
        return true; // Possibly more
    }
    return false;
  }
}

In questo modo puoi sempre utilizzare 'response.IsSuccess ()' per verificare se la tua richiesta è andata a buon fine. E se no, puoi controllare ResponseCode per la risposta.

Opzionalmente puoi aggiungere un'altra proprietà stringa che conterrà la risposta non elaborata, a scopo di debug.

    
risposta data 18.02.2017 - 23:15
fonte
2

Questo non è ciò che i generici sono

Con un metodo generico, stai dicendo che esiste una famiglia infinita di overload per il tuo metodo, ognuno dei quali restituisce un tipo diverso. Qualsiasi sito di chiamata specifico utilizzerà uno di questi overload, restituendo quel tipo specifico.

L'affermazione relativa al problema equivale a "Devo restituire una stringa, o un valore booleano o un int, in ogni sito di chiamata ", non "Alcuni luoghi richiedono una chiamata che restituisce stringhe, altri posti una chiamata che restituisce int, e ancora altri posti che restituiscono bool "

Stai già utilizzando i generici come devono essere usati

Hai TaskAwaiter<T> e TaskCompletionSource<T> utilizzati nel tuo metodo, che sono famiglie di classi che hanno un'implementazione condivisa, ma forniscono metodi che hanno firme diverse, che variano in base al tipo di posto in T . Qui vuoi un tipo specifico, come @Bjarke Søgaard's WebResponse<string> , che viene costruito dai parametri dell'evento.

    
risposta data 20.02.2017 - 11:24
fonte
0

Is it possible to convert this method in this way?

No. Poiché i tipi generici vengono determinati in tempo di compilazione . Nel tuo caso, è possibile solo determinare il tipo in tempo di esecuzione .

private TaskAwaiter<WebResponse> LoadWebPage(string url)
{
    TaskCompletionSource<WebResponse> taskCompletionSource = new TaskCompletionSource<WebResponse>();
    var handler = new WebBrowserDocumentCompletedEventHandler((s, e) =>
    {
        //taskCompletionSource.SetResult(e.Url + ": " + webBrowser1.Document.Title);
        taskCompletionSource.SetResult(<< Stuck Here >>);
    });
    webBrowser1.DocumentCompleted += handler;
    taskCompletionSource.Task.ContinueWith(s => { webBrowser1.DocumentCompleted -= handler; });

    webBrowser1.Navigate(url);
    return taskCompletionSource.Task.GetAwaiter();
}

Perché non utilizzare le attività invece di TaskAwaiter:

private Task<WebResponse> LoadWebPageAsync(string url)
{
    TaskCompletionSource<WebResponse> taskCompletionSource = new TaskCompletionSource<WebResponse>();
    var handler = new WebBrowserDocumentCompletedEventHandler((s, e) =>
    {
        //taskCompletionSource.SetResult(e.Url + ": " + webBrowser1.Document.Title);
        taskCompletionSource.SetResult(<< Stuck Here >>);
    });
    webBrowser1.DocumentCompleted += handler;
    taskCompletionSource.Task.ContinueWith(s => { webBrowser1.DocumentCompleted -= handler; });

    webBrowser1.Navigate(url);
    return taskCompletionSource.Task;
}

Utilizzo:

private async Task ProcessUrlsAsync(string url)
{                  
    var result = await LoadWebPageAsync(url);
    // process result ...
}
    
risposta data 21.02.2017 - 13:47
fonte

Leggi altre domande sui tag