Inheritance Problem

G

Gaz

This is as barebones as I can get it in order to explain my problem.

****************************************************************************
using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
PokerTable t = new PokerTable();
Console.WriteLine(t.ToString());
Console.ReadLine();
}
}
}

public class CardPlayer
{
public string name;
}

public class PokerPlayer : CardPlayer
{
public int chips = 1000;
}

public class CardTable
{
public CardPlayer[] player;

public CardTable()
{
player = new CardPlayer[5];
fillseats();
for (int i = 0; i < player.Length; i++)
player.name = i.ToString();
}

protected virtual void fillseats()
{
for (int i = 0; i<player.Length; i++)
player = new CardPlayer();
}

public override string ToString()
{
StringBuilder s = new StringBuilder();
for (int i = 0; i < player.Length; i++)
{
Console.WriteLine("Player " + player.name + ".");
}
return s.ToString();
}
}

public class PokerTable : CardTable
{
public PokerTable()
{
}

protected override void fillseats()
{
for (int i = 0; i < player.Length; i++)
player = new PokerPlayer();
}

public override string ToString()
{
StringBuilder s = new StringBuilder();
for (int i = 0; i < player.Length; i++)
{
s.AppendLine("Player " + player.name + " has " +
((PokerPlayer)player).chips + " chips.");
}
return s.ToString();
}
}
****************************************************************************


A PokerTable 'is' a CardTable and a PokerPlayer 'is' a CardPlayer.

A CardTable 'has' CardPlayers and a PokerTable 'has' PokerPlayers.

The only annoying thing is that every time I want to refer to the
player.chips member within PokerTable, I have to explicitly cast
player to a PokerPlayer first in order to get at it.

What I want is for player to 'be' a PokerPlayer within the PokerTable
class without having to cast it every time. I wouldn't mind so much if
CardPlayer[] player could be cast to PokerPlayer[] but I can't get that to
work.

Any ideas how to implement this idea?
 
P

Peter Duniho

[...]
What I want is for player to 'be' a PokerPlayer within the PokerTable
class without having to cast it every time. I wouldn't mind so much if
CardPlayer[] player could be cast to PokerPlayer[] but I can't get that
to work.

Any ideas how to implement this idea?


The simplest method I see based on your current design would be to add a
new property that you use to alias the original array:

public PokerPlayer[] pokerplayers
{
get { return (PokerPlayer[])player;
}

Then whenever you want an array of PokerPlayer instances, you use
"pokerplayers" instead of "player".

Alternatively, you could provide a method that retrieves a specific index
and does the casting for you:

public PokerPlayer PokerPlayerFromIndex(int i)
{
return (PokerPlayer)player;
}

Pete
 
J

Jon Skeet [C# MVP]

Peter Duniho said:
What I want is for player to 'be' a PokerPlayer within the PokerTable
class without having to cast it every time. I wouldn't mind so much if
CardPlayer[] player could be cast to PokerPlayer[] but I can't get that
to work.

Any ideas how to implement this idea?


The simplest method I see based on your current design would be to add a
new property that you use to alias the original array:


Alternatively, you could provide a method that retrieves a specific index
and does the casting for you:

<snip>

If you're using .NET 2.0, you could make CardTable generic:

public class CardTable<T> where T : CardPlayer

then use "T" everywhere in CardTable, and declare PokerTable like this:

public class PokerTable : CardTable<PokerPlayer>
 
G

Gaz

Jon Skeet said:
Peter Duniho said:
What I want is for player to 'be' a PokerPlayer within the
PokerTable
class without having to cast it every time. I wouldn't mind so much if
CardPlayer[] player could be cast to PokerPlayer[] but I can't get that
to work.

Any ideas how to implement this idea?


The simplest method I see based on your current design would be to add a
new property that you use to alias the original array:


Alternatively, you could provide a method that retrieves a specific index
and does the casting for you:

<snip>

If you're using .NET 2.0, you could make CardTable generic:

public class CardTable<T> where T : CardPlayer

then use "T" everywhere in CardTable, and declare PokerTable like this:

public class PokerTable : CardTable<PokerPlayer>



Thanks guys, I like the generic idea I will try that and see how far I get
with it.

Btw using a property to alias the original array like this:

public PokerPlayer[] pokerplayers
{
get { return (PokerPlayer[])player; }
}

doesn't work, I already tried it a few times in different forms. It
compiles ok but I get an InvalidCastException at runtime, 'Unable to cast
object of type 'CardPlayer[]' to type 'PokerPlayer[]'.'
 
P

Peter Duniho

[...]
Btw using a property to alias the original array like this:

public PokerPlayer[] pokerplayers
{
get { return (PokerPlayer[])player; }
}

doesn't work, I already tried it a few times in different forms. It
compiles ok but I get an InvalidCastException at runtime, 'Unable to cast
object of type 'CardPlayer[]' to type 'PokerPlayer[]'.'

Huh. I could swear that worked when I tried it. Sorry if I offered bad
advice. My second option should work though for sure. :)

Oh well...I like Jon's generic idea better anyway. I tried casting a
generic array but couldn't get that to work. Making the whole class based
on a generic class seems like a reasonable alternative to me.

Pete
 
J

Jon Skeet [C# MVP]

Mark Wilden said:
public PokerPlayer[] pokerplayers
{
get { return (PokerPlayer[])player; }
}

This is a common gotcha. PokerPlayer[] and CardPlayer[] are not related to
each other, even though their contents are.

No, that's not right. There's an implicit conversion from PokerPlayer[]
to CardPlayer[], but a CardPlayer[] doesn't become a PokerPlayer[] just
because all of its elements are PokerPlayers.

I suspect you're thinking of generics, where List<string> *isn't*
convertible to List<object>.
 
P

Peter Duniho

Mark Wilden said:
This is a common gotcha. PokerPlayer[] and CardPlayer[] are not related
to each other, even though their contents are.

No, that's not right. There's an implicit conversion from PokerPlayer[]
to CardPlayer[], but a CardPlayer[] doesn't become a PokerPlayer[] just
because all of its elements are PokerPlayers.

Well, that's why I was surprised that you get a run-time error on the
conversion.

After all, you can *explicitly* cast a CardPlayer to a PokerPlayer, as
long as that CardPlayer is indeed a PokerPlayer.

Likewise, it seems to me that the language ought to allow one to cast a
CardPlayer[] to a PokerPlayer[] as long as each member of the array is a
PokerPlayer. Conversely, if the language isn't going to allow that (to
prevent hidden performance pitfalls, for example), then it should emit a
compile-time error instead of waiting until runtime (which is what it does
with generics).

Basically, I don't see anything about the syntax of "PokerPlayer[]
pokerplayers = (PokerPlayer[])cardplayers;" that justifies the compiler
allowing it if it's just going to create a run-time error anyway. Is
there some scenario where the "cardplayers" variable could have been
down-cast from a "PokerPlayer[]" initially, and so the cast would work?

Pete
 
J

Jon Skeet [C# MVP]

Peter Duniho said:
No, that's not right. There's an implicit conversion from PokerPlayer[]
to CardPlayer[], but a CardPlayer[] doesn't become a PokerPlayer[] just
because all of its elements are PokerPlayers.

Well, that's why I was surprised that you get a run-time error on the
conversion.

After all, you can *explicitly* cast a CardPlayer to a PokerPlayer, as
long as that CardPlayer is indeed a PokerPlayer.

Likewise, it seems to me that the language ought to allow one to cast a
CardPlayer[] to a PokerPlayer[] as long as each member of the array is a
PokerPlayer.

No - the array "knows" what kind it is and won't allow itself to be
downcast lower than that. Imagine if it did allow it, and consider this
example:

CardPlayer[] players = new CardPlayer[1];
players[0] = new PokerPlayer();
PokerPlayer[] pokerPlayers = (PokerPlayer[]) players;
players[0] = new WhistPlayer();

PokerPlayer pokerPlayer = players[0];

At that point, something really *bad* has happened... but it's not
clear where it should be. There shouldn't be any chance of the last
line going wrong, but that's the only place where it would make sense
to throw an exception, as prior to that there aren't any errors. But
you can't just let it go without any exceptions, as otherwise
pokerPlayer would be a reference to a WhistPlayer.
Conversely, if the language isn't going to allow that (to
prevent hidden performance pitfalls, for example), then it should emit a
compile-time error instead of waiting until runtime (which is what it does
with generics).

It can't emit a compile-time error, because you could do:

CardPlayer[] players = new PokerPlayer[5];
PokerPlayer[] pokerPlayers = (PokerPlayer[]) players;

No runtime exception.
Basically, I don't see anything about the syntax of "PokerPlayer[]
pokerplayers = (PokerPlayer[])cardplayers;" that justifies the compiler
allowing it if it's just going to create a run-time error anyway. Is
there some scenario where the "cardplayers" variable could have been
down-cast from a "PokerPlayer[]" initially, and so the cast would
work?

See the above example.
 

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

Top