开发者

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.


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.


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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜