Steve
Steve

Reputation: 7867

Work around for deriving from a generic type parameter

I realize it is not possible to derive from a generic type parameter, and I understand all the complications that arise if it is allowed.

So my question is, how do I work around this? I am trying to eliminate some duplicate code and the best solution (which is not a solution because it cannot be done) I have come up with would be to do something like this:

public class Proxy<T> : T where T : CommonBaseClass

The reason I would want to do this is to override some methods in the CommonBaseClass.

Here is some actual code examples from my project:

public class ReportingServiceProxy : ReportingService2010
{
    protected override WebResponse GetWebResponse (WebRequest request)
    {
        WebResponse response = base.GetWebResponse(request);
        // Do override stuff here
        return response;
    }
}

public class ReportingExecutionProxy : ReportExecution2005
{
    protected override WebResponse GetWebResponse (WebRequest request)
    {
        WebResponse response = base.GetWebResponse(request);
        // Do override stuff here
        return response;
    }
}

In the example above the ReportingService2010 & ReportExecution2005 classes both derive from SoapHttpClientProtocol which derives from HttpWebClientProtocol. The override methods GetWebResponse and GetWebRequest both override methods in HttpWebClientProtocol and are identical. These methods are where the code duplication exists which I am trying to refactor out.

For completeness, here is some additional code where I implement the code above:

public abstract class SSRSReportBase<T> where T : new()
{        
    protected T ssrs;
    protected abstract void ServiceLogon();

    public SSRSReportBase()
    {
        ServiceLogon();
        // Do other common constructor work here.
    }
}

// Implementation #1
public class SSRSReportExecution : SSRSReportBase<ReportExecutionProxy>
{
    protected override void ServiceLogon()
    {
        ssrs.LogonUser("LoginName", null, null);
    }

    // Create additional wrapper methods for ReportExecution2005
}

// Implementation #2
public class SSRSReportingService : SSRSReportBase<ReportingServiceProxy>
{
    protected override void ServiceLogon()
    {
        ssrs.LogonUser("LoginName", null, null);
    }

    // Create additional wrapper methods for ReportingService2010
}

The only solution I have come up with is not a possible solution, so obviously it's NOT a solution. Here is the bad code:

public class ReportProxy<T> : T where T : HttpWebClientProtocol, new()
{
    protected override WebResponse GetWebResponse (WebRequest request)
    {
        WebResponse response = base.GetWebResponse(request);
        return response;
    }
}

// Implementation #1
public class SSRSReportExecution : SSRSReportBase<ReportProxy<ReportExecution2005>>
{
}

// Implementation #2
public class SSRSReportingService : SSRSReportBase<ReportProxy<ReportingService2010>>
{
}

Question: So my question is, how can this code be refactored in such a way as to remove the duplicate code (the GetWebRequest and GetWebResponse overrides)?

Upvotes: 2

Views: 1939

Answers (1)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112382

A solution would be to not use generics at all and to use a factory pattern. Instead of having a generic parameter with the BaseClass and the new() constraint. Type everything BaseClass and provide a factory for new(). This can be either a full-fleged factory class or a lightweight factory delegate.

public interface IProtocolFactory
{
    ProtocolBaseClass Create();
}

public class SomeDerivedProtocolFactory : IProtocolFactory
{
    public ProtocolBaseClass Create()
    {
        return new SomeDerivedProtocol();
    }
}

You might even add additional initialization code in the factory.

public class ReportProxy
{
    private IProtocolFactory _factory;

    public ReportProxy(IProtocolFactory factory)
    {
        _factory = factory;
    }

    public void DoSomething()
    {
        ProtocolBaseClass protocol = _factory.Create();
        ...
    }
}

Using a delegate

    private Func<ProtocolBaseClass> _createProtocol;

    public ReportProxy(Func<ProtocolBaseClass> createProtocol)
    {
        _createProtocol= createProtocol;
    }

    public void DoSomething()
    {
        ProtocolBaseClass protocol = _createProtocol();
        ...
    }

Upvotes: 1

Related Questions