Reputation: 3942
class Order { ... }
class OrderA : Order { ... }
class OrderB : Order { ... }
class OrderC : Order { ... }
class OrderD : Order { ... }
private Order GetOrder() { ... }
How can i have a method that dynamically casts the returned Order object to any of the specific ones:
private T GetSpecificOrder<T>(T order) : where T : Order
{
...
}
And i wanna call it like:
var myOrder = GetOrder();
var specificOrder = GetSpecificOrder<Order>(myOrder);
HandleOrder(specificOrder);
I want that specificOrder object is instantiated as one of the sub classes so i can call it like this:
HandleOrder(OrderA o) { ... }
HandleOrder(OrderB o) { ... }
HandleOrder(OrderC o) { ... }
HandleOrder(OrderD o) { ... }
What am I doing wrong?
Upvotes: 2
Views: 119
Reputation: 1502286
It sounds like you're expecting overload resolution to occur at execution time. That doesn't happen... unless you use dynamic
, which may well be the simplest solution for you:
dynamic order = GetOrder(orderId);
HandleOrder(order); // The right overload will be picked at execution time
Another alternative would be to use double-dispatch / the visitor pattern, but personally I find that a bit clunky.
Another alternative would be to make a Dictionary<Type, Action<Order>>
like this:
private static readonly Dictionary<Type, Action<Order>> orderHandlers =
new Dictionary<Type, Action<Order>>
{
{ typeof(OrderA), order => HandleOrder((OrderA) order) },
{ typeof(OrderB), order => HandleOrder((OrderB) order) },
{ typeof(OrderC), order => HandleOrder((OrderC) order) },
{ typeof(OrderD), order => HandleOrder((OrderD) order) },
};
Then just fetch the relevant delegate and call it:
orderHandlers[order.GetType()](order);
Upvotes: 3
Reputation: 1479
You would need to call GetSpecificOrder as the desired derived classed:
var specificOrder = GetSpecificOrder<OrderA>(myOrder);
Or cast it:
var specificOrder = (OrderA)GetSpecificOrder<Order>(myOrder);
And then you can assign whatever properties are unique to the given derived class.
Upvotes: 0
Reputation: 726839
You cannot do this with generics: whatever goes into the angle brackets must be a type that is known at compile time, so this line
var specificOrder = GetSpecificOrder<Order>(myOrder);
will require a cast to become OrderA
, OrderB
, etc.
However, there are at least two ways to work around the situation:
Accept
method to the Order
and its subclasses, ordynamic
, in which case the runtime will perform the correct dispatch for you.Here is how to implement the #1:
interface IOrderHandler {
void HandleOrder(OrderA o);
void HandleOrder(OrderB o);
void HandleOrder(OrderC o);
void HandleOrder(OrderD o);
}
abstract class Order { abstract void Accept(IOrderHandler h); }
class OrderA : Order { void Accept(IOrderHandler h) {h.HandleOrder(this);} }
class OrderB : Order { void Accept(IOrderHandler h) {h.HandleOrder(this);} }
class OrderC : Order { void Accept(IOrderHandler h) {h.HandleOrder(this);} }
class OrderD : Order { void Accept(IOrderHandler h) {h.HandleOrder(this);} }
...
var myOrder = GetOrder();
IOrderHandler handler = ... // Obtain the handler
myOrder.Accept(handler); // This will call the correct HandleOrder
Here is how to implement the #2:
dynamic myOrder = GetOrder();
HandleOrder(myOrder); // Will dispatch based on the runtime type
Upvotes: 2
Reputation: 1609
Try this...
private T GetSpecificOrder<T>(T order)
{
return (T)Convert.ChangeType(T, typeof(T));
}
Upvotes: 0