8

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();
        }
    }
}
Jader Dias
  • 88,211
  • 155
  • 421
  • 625

2 Answers2

12

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
  • 4,890
  • 2
  • 20
  • 18
4

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
  • 380,855
  • 102
  • 628
  • 825
  • 1
    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 Dec 30 '08 at 15:37
  • 1
    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 Dec 30 '08 at 16:48
  • If one wants to be evil, one could declare a derived class `Foo` with a constructor something like `Foo(int x) : Foo( ((String)null).Length ) { }` and a destructor which copies `this` to a static location. Try to construct the instance, catch the exception, force a collection, wait for pending finalizers, and the static location will hold an instance of the derived class without any base-class constructors having run. – supercat Dec 08 '13 at 02:27
  • @supercat Unfortunately there can be no loops in constructor calls. – IS4 Feb 21 '21 at 15:51
  • @IS4: I don't see any loops in the above. – supercat Feb 21 '21 at 16:12
  • @supercat Assuming you meant `: this(`, the constructor of `Foo` calls itself, forming a cycle. This results in a compiler error. – IS4 Feb 21 '21 at 16:35
  • @IS4: Ah. I've used VB.NET much more than C#, though it's been awhile since I've looked much at either; the constructor semantics are a bit different. I think in C# the effect I was looking for could be accomplished by having an object field initializer which throws an exception, since I think in C# those run before the parent constructor call (whereas in VB.NET they run after). – supercat Feb 21 '21 at 18:25
  • @IS4: Alternatively, I think one could simply have a constructor which chains to another constructor, but includes within the chaining call a method that will throw an exception. – supercat Feb 21 '21 at 18:26
  • It doesn't matter, it won't compile since you *have* to call inherited constructors and in this case you can't. – Lasse V. Karlsen Feb 22 '21 at 09:12