Reputation: 2044
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
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
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