bubbleking bubbleking - 27 days ago 7
C# Question

Casting to Less Derived Types and Dealing with Generics

I'm trying to write something capable of taking various behavior classes and linking them together into a chain of queries and commands. One approach I've taken is below, but I'm running into an issue casting derived types to base types. I'm having to deal with generic parameters, and I'm not sure if this approach isn't possible, or if I need to define some implicit or explicit conversion operators, or something else altogether.

Here is the set of base types. First is the most basic interface:

public interface IInvoker
{
void Invoke();
}


This abstract class adds the ability to have a "subject" which is the thing that does the command or query via one of its members. It defers implementing the Invoke method:

public abstract class AbstractInvoker<TSubject> : IInvoker
{
protected TSubject Subject;

public void SetSubject(TSubject subject)
{
Subject = subject;
}

public abstract void Invoke();
}


This next abstract class would be the type implemented by any concrete query class (as opposed to a command, which would not have a TResult type). It sets up the ability to chain queries via the Successor.

public abstract class AbstractQueryInvoker<TSubject, TResult> : AbstractInvoker<TSubject>
{
protected AbstractInvoker<TResult> Successor;

public void SetSuccessor(AbstractInvoker<TResult> successor)
{
Successor = successor;
}

public override void Invoke()
{
var result = DoQuery();

Successor.SetSubject(result);
Successor.Invoke();
}

protected abstract TResult DoQuery();
}


The actual query logic is implemented in concrete classes via the
DoQuery()
method.

I set it up like this so I could chain the queries together like this:

private List<IInvoker> _invokers;

// Build the list of various concrete classes

for (int i = 0; i < _invokers.Count - 1; i++)
{
((AbstractQueryInvoker<dynamic, dynamic>)_invokers[i]).SetSuccessor(
(AbstractInvoker<dynamic>)
_invokers[i + 1]);
}


My aim was to have each invoker, except for the last one, have its successor chained up here so all I would need to do would be to call
Invoke()
on the first element. However, the first cast in the for loop isn't working (and the second might not either, I'm guessing). The error message looks something like this:

{"Unable to cast object of type 'ConcreteQueryInvoker' to type 'AbstractQueryInvoker`2[System.Object,System.Object]'."}


I'm hoping there is some way to get around this without having to implement something particular in each concrete Invoker. I may end up having dozens of these concrete classes, each using different types for the generic type parameters. So, is there a way to remedy this?

Answer

Just cast each invoker to dynamic:

// Build the list of various concrete classes
for (int i = 0; i < _invokers.Count - 1; i++)
{
    ((dynamic)_invokers[i]).SetSuccessor((dynamic)_invokers[i + 1]);
}

Why your original code doesn't work: Although AbstractQueryInvoker<dynamic, dynamic> uses the dynamic keyword, it's not actually a dynamic type—only dynamic by itself is dynamic. At run time, the dynamic type arguments are substituted by object, and so the cast fails.

Comments