views:

822

answers:

2

I'm currently trying to modify some HttpWebRequest functions, but I can't do it through inheritance because HttpWebRequest has no public constructors (besides the deserialization constructor). Is there a workaround to do this?

My objective is to code something like the example below, but this class objects must inherit the HttpWebRequest properties and methods:

using System;
using System.Net;
using System.Threading;

public class AsyncWebRequest:WebRequest
{
    private readonly AsyncCallback getResponseCallback;
    private readonly Uri uri;
    private volatile int RetriesLeft = 3;
    private volatile WebRequest request;

    public AsyncWebRequest(string uri, AsyncCallback getResponseCallback)
       :this(new Uri(uri), getResponseCallback)
    {
    }

    public AsyncWebRequest(Uri uri, AsyncCallback getResponseCallback):base()
    {
        this.uri = uri;
        this.getResponseCallback = getResponseCallback;
    }

    private IAsyncResult BeginGetResponse()
    {
        request = HttpWebRequest.CreateDefault(uri);
        ((HttpWebRequest)request).ReadWriteTimeout = Timeout;
        var result = request.BeginGetResponse(GetResponseCallback, null);
        ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle,
            GetResponseTimeout, null, Timeout, true);
        return result;
    }

    private void GetResponseTimeout(object state, bool timedOut)
    {
        if (timedOut)
        {
            Retry();
        }
    }

    private void Retry()
    {
        request.Abort();
        bool retry = false;
        lock (request)
        {
            if (RetriesLeft > 0)
            {
                Interlocked.Decrement(ref RetriesLeft);
                retry = true;
            }
        }
        if (retry)
        {
            BeginGetResponse();
        }
        else
        {
            getResponseCallback(null);
        }
    }

    private void GetResponseCallback(IAsyncResult AsyncResult)
    {
        try
        {
            getResponseCallback(AsyncResult);
        }
        catch(WebException webException)
        {
            Retry();
        }
    }
}
+3  A: 

Unless you can trick the serialization constructor to do your bidding, then no, there is no way.

The constructors of that class are internal, so you have no way of calling them.

Lasse V. Karlsen
Can't I use the following solution?http://stackoverflow.com/questions/178645/how-does-wcf-deserialization-instantiate-objects-without-calling-a-constructor#179486
Jader Dias
Well, you can of course use reflection and similar things, but that's really going to cause more problems than it's worth in the long run. You're bypassing all configuration of the class, which may mean it's not in a consistent state at all.
Lasse V. Karlsen
+6  A: 

You can't through inheritance from HttpWebRequest (if you don't want to call the serialization constructor) , but you can through composition and delegation, and through inheritance from WebRequest (I'm not sure if that will do it for you, but functionally it is quite similar). WebRequest has a default constructor.

In this case you then can't have the class 'be' a HttpWebRequest (as in an is-a relationship), since you can't extend from it, but it wil 'be' a WebRequest, which should suffice.

You could write a class that inherits from WebRequest, that has an instance member of type WebRequest, create a HttpWebRequest and assign to instance member in the constructor of the new type and delegate all calls to that reference (sort of a decorator pattern):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;

namespace ClassLibrary1
{
    public class MyHttpWebRequest : WebRequest
    {
        private WebRequest request;

        public MyHttpWebRequest(string uri)
        {
            request = HttpWebRequest.Create(uri);
        }

        public override WebResponse GetResponse()
        {
            // do your extras, or just delegate to get the original code, as long
            // as you still keep to expected behavior etc.
            return request.GetResponse();
        }

    }
}
Raymond Roestenburg
He can also delegate the call to the serialization constructor through reflection using this method.
Robert C. Barth
That´ll do the trick.
Jader Dias
+1 for this approach. I generally prefer composition over inheritance.
Drew Noakes