Generics and inheritance

G

Guest

Sorry if this is the wrong group, but I can think not of where else to post
(excepting of course my cross group post to .NET Development/General)

I have an inheritance hierarchy in place as follows ...

PersonVisitor<T> <-- AlphabeticVisitor<T> <-- FamilyNameVisitor<T>

public interface IPerson
{
string GivenName { get; set; }
string FamilyName { get; set; }
IPerson Clone();
void Visit(PersonVisitor<IPerson> visitor);
}

public abstract class PersonVisitor<T> where T : IPerson
{
virtual public void Visit(T c) { }
}

public abstract class AlphabeticVisitor<T> : PersonVisitor<T> where T :
IPerson
{
// class code omited
}

public class FamilyNameVisitor<T> : AlphabeticVisitor<T> where T : IPerson
{
override public void Visit(T c)
{
}
}

Firstly, I would expect that I could pass an object of type
FamilyNameVisitor<T> as follows

Customer c = PeopleFactory.CreateNewCustomer("Marcus", "Barnard");
Assert.IsNotNull(c, "Failed to instantiate the Customer object");
FamilyNameVisitor<Customer> v = new FamilyNameVisitor<Customer>();
c.Visit(v);

However, I am told that this is not possible because an object of type
FamilyNameVisitor<Customer> cannot be converted to type
PersonVisitor<IPerson> (even though FamilyNameVisitor<Customer> is a
sub-class of PersonVisitor<IPerson>, AND Customer is a sub-class of IPerson)

So, I then put the following implicit conversion into the FamilyNameVisitor
class definition

public static implicit operator
PersonVisitor<IPerson>(FamilyNameVisitor<T> v)
{
return (v as PersonVisitor<IPerson>);
}

but this conversion always returns null :blush:(

Can somebody please help?

Thanks in advance
 
J

Jon Skeet [C# MVP]

However, I am told that this is not possible because an object of type
FamilyNameVisitor<Customer> cannot be converted to type
PersonVisitor<IPerson> (even though FamilyNameVisitor<Customer> is a
sub-class of PersonVisitor<IPerson>, AND Customer is a sub-class of IPerson)

Indeed, and it's right. What you're after is variance applied to
generics - and it doesn't exist in C# at the moment. For instance, a
List<string> can't be used as a List<object>, which is actually good
because you could add a new object() to a List<object>...

See Eric Lippert's blog for more details:
http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

Jon
 
G

Guest

Jon,

thanks for that, sometimes it just takes someone stating the obvious to
highlight the errors of ones ways, since of course I do not want to be able
to add SomeObject to my generic list List<SpecificObject>,

however, what I don't quite understand is that if
FamilyNameVisitor<Customer> is a sub-class of PersonVisitor<IPerson> why can
I not use FamilyNameVisitor<Customer> in place of PersonVisitor<IPerson> and
why does my implicit operator return null?


--
Of all words of tongue and pen, the saddest are: "It might have been"

Bill.Richards @ greyskin .co .uk
http://greyskin.co.uk
 
G

Guest

For example ... I can do the following

public List<T> SortByFamilyName(string name)
{
FamilyNameVisitor<T> visitor = new FamilyNameVisitor<T>(name);
return (SortAlphabetically(visitor, name));
}

private List<T> SortAlphabetically(AlphabeticVisitor<T> visitor,
string name)
{
foreach (T item in this)
visitor.Visit(item);

return (visitor.SortedAlphabetically);
}


--
Of all words of tongue and pen, the saddest are: "It might have been"

Bill.Richards @ greyskin .co .uk
http://greyskin.co.uk
 
J

Jon Skeet [C# MVP]

thanks for that, sometimes it just takes someone stating the obvious to
highlight the errors of ones ways, since of course I do not want to be able
to add SomeObject to my generic list List<SpecificObject>,

however, what I don't quite understand is that if
FamilyNameVisitor<Customer> is a sub-class of PersonVisitor<IPerson> why can
I not use FamilyNameVisitor<Customer> in place of PersonVisitor<IPerson>

FamilyNameVisitor<Customer> isn't a subclass of
PersonVisitor<IPerson>. It's a subclass of FamilyNameVisitor<IPerson>,
just as a List said:
and why does my implicit operator return null?

For exactly the same reason as a cast would fail - a
FamilyNameVisitor<Customer> just *isn't* a PersonVisitor<IPerson>, so
the "as" returns null. Change it to use a cast and you'll get an
exception instead.

Jon
 
G

Guest

Jon Skeet said:
FamilyNameVisitor<Customer> isn't a subclass of
PersonVisitor<IPerson>. It's a subclass of FamilyNameVisitor<IPerson>,
just as a List<string> isn't a List<object> or even an IList<object>.

but I think however that instead ...

FamilyNameVisitor<Customer> --> AlphabeticVisitor<T> -->
PersonVisitor<Customer>

and not

FamilyNameVisitor<Customer> --> FamilyNameVisitor<IPerson>

otherwise, the following would surely be true?

FamilyNameVisitor<Customer> --> FamilyNameVisitor<IPerson> -->
AlphabeticVisitor<IPerson> --> PersonVisitor<IPerson>

because FamilyNameVisitor<T> is a sub-class of PersonVisitor<T>.

The problem is I think that although the template parameter MUST implement
IPerson, the generic created class actually knows nothing about this, because
the type that is passed all the way up through the hierarchy is not IPerson,
but instead the concrete class, which explains why the cast fails and as you
said
a FamilyNameVisitor<Customer> just *isn't* a PersonVisitor<IPerson>, so
the "as" returns null.

What I'm actually trying to do is tie in the Visitor to the IPerson
implementation, by defining the Visit member on the interface, which I
thought I could achieve by specifying the argument for such a member as
PersonVisitor<IPerson> since all Visitor<T> extend PersonVisitor<T> and all T
implement IPerson, but now I'm thinking that maybe instead the interface
member should be PersonVisitor<T> ... or do you think I'm barking up
completely the wrong tree.
 
J

Jon Skeet [C# MVP]

What I'm actually trying to do is tie in the Visitor to the IPerson
implementation, by defining the Visit member on the interface, which I
thought I could achieve by specifying the argument for such a member as
PersonVisitor<IPerson> since all Visitor<T> extend PersonVisitor<T> and all T
implement IPerson, but now I'm thinking that maybe instead the interface
member should be PersonVisitor<T> ... or do you think I'm barking up
completely the wrong tree.

I'm afraid I'm not really clear on exactly what the purpose of the
code is in the first place, but one common way of getting round this
kind of thing is to change from having a generic *type* to a generic
*method*, and then use type inference to get the compiler to work it
all out.

Jon
 
G

Guest

Thanks for your time and explanatory efforts Mr Skeet, my previous post was
actually approaching the correct answer, and I don't think I would have
gotten there today had it not been for your insight :blush:)

Of course I was making the mistake of believing that the generics classes
know more about themselves than they actually do ... the solution is thus ...

Change the IPerson interface:

public interface IPerson
{
string GivenName { get; set; }
string FamilyName { get; set; }
void Visit<T>(PersonVisitor<T> visitor) where T: IPerson;
}

Then change my abstract Person (IPerson partial implementation)'s Visit
member to properly implement the interface

public abstract void Visit<T>(PersonVisitor<T> visitor) where T : IPerson;

Then change my Customer class

override public void Visit<T>(PersonVisitor<T> visitor)
{
visitor.Visit(this);
}

and in the words of Fred Dibnah (if you've heard of him) ... Job's a good un'

Thanks again Jon
 
J

Jon Skeet [C# MVP]

Thanks for your time and explanatory efforts Mr Skeet, my previous post was
actually approaching the correct answer, and I don't think I would have
gotten there today had it not been for your insight :blush:)

Of course I was making the mistake of believing that the generics classes
know more about themselves than they actually do ... the solution is thus ...

Change the IPerson interface:

public interface IPerson
{
string GivenName { get; set; }
string FamilyName { get; set; }
void Visit<T>(PersonVisitor<T> visitor) where T: IPerson;
}

Excellent - just the suggestion I was posting in parallel :)
and in the words of Fred Dibnah (if you've heard of him) ... Job's a good un'

I certainly have. The world is a poorer place without him.

Jon
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top