C# – Converting Method to Return Generic Type

cgenericsreturn-type

I would like to ask how to convert a method so that it can return any type depending on what happens within it.

What I have is the start of an application which will visit a number of webpages (and ultimately perform some tasks on each) before moving on to the next.

At the minute I have this:

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

What I'd like to do is change the

private TaskAwaiter<string> LoadWebPage(string url)

method so that it returns a TaskAwaiter< generic type >. My reason for this is that if it fails to log in or an error occurs whilst processing, then I can return a boolean or a string or an int to signify the success (or not) of the actions being performed.

I initially thought of overloading the method until I realised this would not work with different return types, doh!!

The references to the web pages are only there as a test to make sure I can get the basics of opening a web page. Once I have this, these will be replaced with my systems web pages before I can start processing.

Is it possible to convert this method in this way?

Edit:
I like the look of @Bjarke Søgaard's solution so am trying to implement it.

Unfortunately I'm being a little dense at this time of night and for the life of me cannot work out how to use within the method. So far I have this:

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

As you can see I'm at a loss as to how to set the result. Any help would be appreciated with this.

Thanks

Best Answer

How about returning something like this?

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

This way, you can always use 'response.IsSuccess()' to check if your request went through all right. And if not, you can check ResponseCode for the reply.

Optionally you can add another string property that'll contain the raw response, for debugging purposes.