C#: is System.Type not a real type? or: working around single dispatch

81 views Asked by At

I've been trying to work around C# not having polymorphic dispatch based on the method argument type, and I have encountered that you can't pass types around.

I basically have an abstract class Model that implements two methods: IEnumerable<Decision> GetDecisions() and void TakeDecision(Decision decision). Decision is an abstract class as well. The consumer of this class repeatedly gets possible decisions, evaluates them and passes the best one back to the Model.

Each single derived model can work with some common decisions and some model-specific decisions and I have a separate TakeDecision() method for each Decision type this specific Model can work with. The problem is of course with single dispatch. Ideally, the consumer would do this:

var m = ModelFactory.GetModel(some parameters); //m is type Model var ds = m.GetDecisions(); //ds is IEnumerable<Decision> //Some logic here to choose the best Decision d m.TakeDecision(d);

Now I have to implement logic that looks like this in every derived Model, because C# can dispatch to the correct Model implementation, but not to the correct overload:

if (decision is FooDecision) TakeDecision((FooDecision)decision); if (decision is BarDecision) TakeDecision((BarDecision)decision); ...

or I force the consumer to do the casting on their side (they have most likely already done it to examine the decision).

I wanted to have a list of System.Types in each derived class, so I could do this:

foreach (var t in AllowedDecisionTypes) { if (decision is t) TakeDecision((t)decision); }

but it looks like System.Type is not a real type:

  1. You can't do this: AllowedDecisionTypes.Add(FooDecision), but you can do AllowedDecisionTypes((new FooDecision()).GetType())
  2. And vice versa, you can't do decision is AllowedDecisionTypes[0], but you can do decision is FooDecision.

Is there a way to have both? I.e., generate a list of types and cast to them? Or is the only way out to do double dispatch and implement void Decision.ApplyTo(Model model) { model.TakeDecision(this); }, on each decision, which should probably dispatch to the correct overload, since this is now a specific Decision?

2

There are 2 answers

2
Servy On

To add a Type to a list of Type objects you simply need to use the typeof operator instead of just adding the type.

The equivalent operation to is for a Type objects is to use its IsAssignableFrom method.

You won't be able to cast an object based on a Type; the way to call one of multiple overloads based on a Type is through reflection.

0
Moti Azu On

Type is an object describing a certain type, but it's not the type itself if we look at it this way: String != typeof(String).

What you can do is this:

// Add type to collection of Type
AllowedDecisionTypes.Add(typeof(FooDecision))

// Check if the current decision EXACTLY of the same type
if(myDecision.GetType() == AllowedDecisionTypes[0])

// Check if the current decision inherits/implements that type (like using the 'is' operator)
if(AllowedDecisionTypes[0].IsAssignableFrom(myDecision.GetType()))