ajmccall
ajmccall

Reputation: 2044

C# Compilation Error When Trying To Implement An Abstract Generic Class On An Extended Type T

I am using generics and feel I've gone in too deep over my head and was wondering if StackOverflow could help? I think it would be much easier to explain my question using a simplified version of my code, rather than an abstract example of class A extending class B etc. I do apologise though if it is too simplistic.

My C# (Windows Phone 7 .NET 3.5) application fires requests to a webservice and consumes the XML response populating a Result object derived from a base class WebServiceResult. Originally it fired the request, parsed the response and then the calling method would type cast the Result into the result it was expecting. I thought that since we know what type of result we are expecting, this was pointless and tried to use generic to over come this.

// abstract class to do the raw http response handling
public abstract class WebServiceResultParser<T> where T : WebServiceResult {
    T result;
    public WebServiceResultParser(T result) {
        this.result = result;
    }

    protected abstract bool Parse(String response);
    private bool ParseHttpResponse(HttpWebResponse httpWebResponse){
        //some logic to get http response as string
        result.GetParser<T>().Parse(http_response_as_string);
        return true;
    }
}

// abstract class that models a webservice result
public abstract class WebServiceResult {
   protected internal abstract WebServiceResultParser<T> GetParser<T>() 
                                                where T : WebServiceResult;
}

Implementation of "Registration" webservice request

// knows how to parse the xml
public class RegistrationResultParser : WebServiceResultParser<RegistrationResult>{
    private RegistrationResult result;
    public RegistrationResultParser(RegistrationResult result)
        : base(result) {
            this.result = result;
    }

    protected override bool Parse(String response){
        //some logic to extract customer number
        result.CustomerNumber = customerNumber;
        return true;
    }
}
// stores the result
public class RegistrationResult : WebServiceResult {
    public String CustomerNumber { get; internal set; }

    protected internal override WebServiceResultParser<T> GetParser<T>() {
        return new RegistrationResultParser(this); // <--- Compiler error here
    }
}

The compiler error says

Cannot implicitly convert type 'RegistrationResultParser' to 'WebServiceResultParser<T>'

This is about as far as I can go without going round and round in circles. Any advice, further reading or comments would be appreciated.

Cheers, Alasdair.

Upvotes: 2

Views: 312

Answers (2)

gjvis
gjvis

Reputation: 106

The class structure as it stands doesn't compile because the .NET framework isn't covariant or contravariant on generics (well, it isn't pre .NET 4.0). A good article about covariance and contravariance with generics (including how to specify them in .NET 4.0) can be found here: http://msdn.microsoft.com/en-us/library/dd799517.aspx

That is kind of clouding the issue though, as I don't think adding variance support to your class hierarchy is the answer.

If I'm understanding your intention correctly - in RegistrationResult you're trying to say that you want the GetParser() method to return you a WebResultParser<RegistrationResult> (as opposed to a WebResultParser for any other type of WebServiceResult).

To make it work like that, you need to make the following changes (annotations in the comments):

// update the constraint to match the new WebServiceResult definition
public abstract class WebServiceResultParser<T> where T : WebServiceResult<T>
{
    T result;
    public WebServiceResultParser(T result)
    {
        this.result = result;
    }

    protected abstract bool Parse(String response);
    private bool ParseHttpResponse(HttpWebResponse httpWebResponse)
    {
        //some logic to get http response as string
        var http_response_as_string = httpWebResponse.ToString();
        result.GetParser().Parse(http_response_as_string);
        return true;
    }
}

// move the type constraint from the GetParser() method to the class itself
public abstract class WebServiceResult<T> where T : WebServiceResult<T>
{
    protected internal abstract WebServiceResultParser<T> GetParser();
}

// no change
public class RegistrationResultParser : WebServiceResultParser<RegistrationResult>
{
    private RegistrationResult result;
    public RegistrationResultParser(RegistrationResult result)
        : base(result)
    {
        this.result = result;
    }

    protected override bool Parse(String response)
    {
        //some logic to extract customer number
        //result.CustomerNumber = customerNumber;
        return true;
    }
}

// explicitly specify the constraint on the base class (which is used to
// specify the GetParser() return type)
public class RegistrationResult : WebServiceResult<RegistrationResult>
{
    public String CustomerNumber { get; internal set; }

    protected internal override WebServiceResultParser<RegistrationResult> GetParser()
    {
        return new RegistrationResultParser(this);
    }
}

It was breaking before because because RegistrationResultParser inherits from WebServiceResultParser<RegistrationResult> only, and not from WebServiceResultParser<T>

The whole A : B<A> does look a little strange, but it does achieve what you're trying to do.

Upvotes: 3

Justin Niessner
Justin Niessner

Reputation: 245419

The way you have your code written, there's no guarantee that GetParser<T> will return a WebServiceResultParser<RegistrationResult> (which is what the RegistrationResultParser inherits from) so you can't compile.

I think it should work fine if you change the method to:

protected internal override WebServiceResultParser<RegistrionResult> 
    GetParser<RegistrationResult>()
{
    return new RegistrationResultParser(this);
}

Upvotes: 3

Related Questions