.NET Questions (CLOSED)

Questions and Answers on any aspect of .NET. Now closed.

This discussion group is now closed.

Have a question about .NET development? Try stackoverflow.com, a worldwide community of great developers asking and answering questions 24 hours a day.

The archives of .NET Questions contain years of Q&A. Even older .NET Questions are still online, too.

Polymorphism question

Suppose I have three types: IVehicle, IBus, and ITrain. IBus and ITrain both implement IVehicle. I want to do something like the following code:

IVehicle v = this.GetVehicle();
SmashVehicle(v);

void SmashVehicle(ITrain v) { ... }
void SmashVehicle(IBus v) { ... }

but instead I am forced to write this:

IVehicle v = this.GetVehicle();
if(v is ITrain) SmashVehicle((ITrain) v);
else if(v is IBus) SmashVehicle((IBus) v);

void SmashVehicle(ITrain v) { ... }
void SmashVehicle(IBus v) { ... }

Is there any way around this?
Max Eskin Send private email
Friday, March 17, 2006
 
 
Put smash into Ivehicle.
son of parnas
Friday, March 17, 2006
 
 
interface IVehicle
{
  void Smash();
}

class IBus : IVehicle
{
  void Smash() { // do stuff }
}

class ITrain : IVehicle
{
  void Smash() { // do other stuff }
}


// in some method
IVehicle v = this.GetVehicle();
v.Smash();
Eric Wise Send private email
Friday, March 17, 2006
 
 
That's the easy solution . . . but suppose I don't want to do that? For instance, I might have a class called Seat which has constructors

public Seat(IBus seat) and
public Seat(IPlane seat)

but I don't want to define Bus.GetSeat() and Plane.GetSeat() because I might also have a class Tire and Antenna and Window that also have constructors for Bus and Plane, but I don't want to have to define a new method for all my vehicles every time I define a new class.
Max Eskin Send private email
Friday, March 17, 2006
 
 
Use the vistor pattern.

Use AOP and change the classes.
son of parnas
Friday, March 17, 2006
 
 
Ok, then change interface IVehicle to interface ISmashable.  You can't expect to be able to treat a set of classes as polymorphic unless you define some common interface between them. 

Your example with Seat, IBus and IPlane is a little confusing because it seems to be a little backwards from the logical design.  That is, it's not natural to think of a seat as owning a bus or a plane but rather for a bus or plane to own seats.  The flaw in the Seat class is that the constructors take an IBus or an IPlane directly.  When you want to add boats and spaceships , you'll have to change the Seat class to add these constructors.  If Seat really needs a reference to its owner (which isn't unusual, say a window having a reference to its parent window), it would be best to factor out the functionality that Seat needs from its owner into a separate interface implemented by the owner classes (ISeatContainer, ISeatSite, whatever).  Then when you need to implement Boat, you can just derive it from ISeatContainer and it'll work with the Seat class without you having to mess with the Seat class.
SomeBody Send private email
Saturday, March 18, 2006
 
 
What I was hoping for is that there would be a way to make the program attempt to cast IVehicle to IBus, ITrain, etc. in turn at runtime, and if it failed, to throw a typeexception. All of the replies are correct, of course, but they assume that there isn't a significant amount of pre-existing code that I am loathe to modify.
Max Eskin Send private email
Sunday, March 19, 2006
 
 
How about reflection then?  You could write a method that iterates through the available SmashVehicle (or whatever) implementations looking for matches to the parameter.  This'll get the job done as long as performance isn't important.
SomeBody Send private email
Sunday, March 19, 2006
 
 
Max, what you want is known as "duck typing". When the compiler sees a method invocation that can't be resolved from the known type of the object, the method's name and signature are stored and its resolution is deferred until run time. The run time system then looks if any methods of the run-time object match the stored method name and signature.

C# and VB.NET do not offer this feature, nor most other languages. Python does, however. You can simulate duck typing using a reflection lookup of the method name (in fact that's how duck typing works internally) but of course it's slow.
Chris Nahr Send private email
Monday, March 20, 2006
 
 
Hmm, I think I'm confused about what "duck typing" means... I think it's really about inferring the type of an object from the way that it's used. Regardless, other than this term my previous post should be correct.
Chris Nahr Send private email
Monday, March 20, 2006
 
 
cheap calls Send private email
Monday, March 20, 2006
 
 
You could in theory simply push the if statement down into the SmashVehicle function, however any way you cut it you're still going to have to have different ways to smash each type of vehicle (unless they all are smashed the same way using only IVehicle's data members, which is doubtful).

If you already have a lot of existing code you don't want to move around too much, go with the suggestion of adding .Smash functions into each of the types.  IVehicle's function smashes everything it knows about, then IBus' function smashes the bus stuff before calling base.Smash, similarly with ITrain, IPlane, and IAutomobile.  As this would be an addition to the existing classes, rather than changing a class into an interface, it won't incur a major overhaul of anything, and you only have to do the IVehicle smashing once instead of in each and every derivative.  Also, if you ever do change one of the classes, you can change the relevant parts of Smash very easily.

Any attempts to move this out of the class will likely end up being quite slow and quite muddled and certainly will entail more code than adding the functionality to your classes.
Ryan Schwab Send private email
Monday, March 20, 2006
 
 
Seconding the use of the visitor pattern

http://en.wikipedia.org/wiki/Visitor_pattern has some good demos
Matt B Send private email
Monday, March 20, 2006
 
 
It's better to add Smash() or GetSeat() to IVehicle, but when you can't or don't want to add to the original classes you can create a mirror set of classes with a class factory at the top:

(sorry I need to use C# syntax)

public interface IVehicleExtras
{
    void Smash();
    ISeat Seat;
}

public sealed class VehicleExtrasFactory
{
    public static IVehicleExtras CreateVehicleExtras(IVehicle vehicle)
    {
        ITrain train = vehicle as ITrain;
        if(train != null)
        {
            return new TrainExtras(train);
        }
        IBus bus = vehicle as IBus;
        if(bus != null)
        {
            return new BusExtras(bus);
        }
        throw new InvalidArgumentException("blah blah");
    }
}

public class TrainExtras : IVehicleExtras
{
    TrainSeat seat = new TrainSeat();

    public void Smash()
    {
    }

    public ISeat Seat
    {
        get{ return seat; }
    }
}

public class BusExtras : IVehicleExtras
{
    BusSeat seat = new BusSeat();

    public void Smash()
    {
    }

    public ISeat Seat
    {
        get{ return seat; }
    }
}
Send private email
Tuesday, March 21, 2006
 
 
Generics??
Austro Boy
Tuesday, March 21, 2006
 
 
How would you use generics to solve this?
SomeBody Send private email
Tuesday, March 21, 2006
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz