Using generics while trying to access sub class member

I have these objects:

class Order { ... }

class OrderA : Order { public double X; }

class OrderB : Order { public double X; }

class OrderC : Order { public double Y; }

And i want to do a method that receives any of the 3 subclasses of Order object like this:

private DoSomething<T>(T order) where T : Order
{
    double myValue = order is OrderA || order is OrderB ? (T)order.X : (T)order.Y;

    // DO SOMETHING MORE
}

But i can't compile my code because "T does not contain a definition for X..." Is this possible?

Upvotes: 1

Views: 51

Answers (3)

E-Bat
E-Bat

Reputation: 4892

In Order classs define an abstract method and override it in each subclass. That way each class can do the job without cast the order instances. As @Crono said, that will defeats the purpose of generics.

//In Order class
public abstract void DoIt(){}

//In OrderSubClass class
public override void DoIt(){
  //Do Something with X or Y
}

//Client code
private void doSomething( Order theOrder){
  theOrder.DoIt();
}

If the processing is more complex, i recomend you to stick with template method pattern:
Template Method Pattern

Upvotes: 0

Crono
Crono

Reputation: 10488

Since X / Y members haven't been declared at the core Order class, the DoSomething method cannot possibly see them. Of course you can cast your order value to the type you want, but that defeats the purpose of using a generic method to begin with.

I personnally have a rule of thumb with generics: whenever I require casting on a generic type, I'm most certainly doing something wrong.

If all you need is to get the appropriate value before doing "something more", I suggest you use overloaded methods instead. These could in turn call a "core" method with appropriate arguments:

private void DoSomething(OrderA order)
{
    DoSomethingMore(order, order.X);
} 

private void DoSomething(OrderB order)
{
    DoSomethingMore(order, order.X);
} 

private void DoSomething(OrderC order)
{
    DoSomethingMore(order, order.Y);
} 

private void DoSomethingMore(Order order, double value)
{
    // do something more
}

Upvotes: 2

p.s.w.g
p.s.w.g

Reputation: 149068

This would work:

double myValue = 
    (order is OrderA) ? ((OrderA)order).X :
    (order is OrderB) ? ((OrderB)order).X :
    (order is OrderC) ? ((OrderC)order).Y;

However, having to do this kind of test inside a generic, kind of defeats the purpose of using generics.

You could provide different overloads of DoSomething to handle each case you expect to receive:

private DoSomething(OrderA order)
{
    double myValue = order.X;
    // DO SOMETHING MORE
}

private DoSomething(OrderB order)
{
    double myValue = order.X;
    // DO SOMETHING MORE
}

private DoSomething(OrderC order)
{
    double myValue = order.Y;
    // DO SOMETHING MORE
}

Then you will probably want to wrap that 'DO SOMETHING MORE' up in a private method to use as much common logic as possible.

Alternatively, you could create a virtual or abstract method in Order, like this:

class Order {
    public virtual double GetValue() { ... }
}

class OrderA : Order { 
    public double X; 
    public override double GetValue() { return this.X; }
}

class OrderB : Order { 
    public double X; 
    public override double GetValue() { return this.X; }
}

class OrderC : Order { 
    public double Y; 
    public override double GetValue() { return this.Y; }
}

...

private DoSomething(Order order)
{
    double myValue = order.GetValue();
    // DO SOMETHING MORE
}

Notice, neither solution needed to make use of generics.

Upvotes: 4

Related Questions