I have a table controller that I'm using for several tables (Data Models). Here is a simplified version of it:
public abstract class TableBase
{
public virtual void TableName()
{
Console.WriteLine("I am Table: " + this.GetType().Name);
}
}
public class TableA : TableBase
{
}
public class TableB : TableBase
{
}
public class TableC : TableBase
{
}
public class Controller<T> where T : TableBase
{
public Controller(T table)
{
table.TableName();
}
public void Synchronize();
}
Then I basically use it like this:
Controller<TableA> Controller1 = new Controller<TableA>(new TableA());
Controller<TableB> Controller2 = new Controller<TableB>(new TableB());
Controller<TableC> Controller3 = new Controller<TableC>(new TableC());
Everything is easy breezy but the problem comes when I want to add the controllers to a list of controllers:
List<Controller<TableBase>> ControllerList = new List<Controller<TableBase>>();
ControllerList.Add(Controller1);
ControllerList.Add(Controller2);
ControllerList.Add(Controller3);
It tells me that I can't convert Table(A,B,C) to type of TableBase, for some reason using the base as a type in the controller class freaks everything out. I wouldn't think this is causing a variance issue but it seems to be. All I'm wanting to do is call Synchronize()
on each controller in a loop. How do i get this to work?
If you need to call a common method in otherwise incompatible types, you can define an interface that exposes the functionality you need to call.
In this code sample, I added a new interface ICanSync
which has method signature matching Synchronize()
from your Controller<T>
, and modified Controller<T>
to implement the new interface. This means you can create a List<ICanSync>
and add controllers with incompatible generic types.
// new interface to use
public interface ICanSync
{
void Synchronize();
}
public abstract class TableBase
{
public virtual void TableName()
{
Console.WriteLine("I am Table: " + this.GetType().Name);
}
}
public class TableA : TableBase
{
}
public class TableB : TableBase
{
}
public class TableC : TableBase
{
}
public class Controller<T> : ICanSync where T : TableBase
{
private TableBase _t;
public Controller(T table)
{
_t = table;
}
public void Synchronize()
{
_t.TableName();
}
}
You can declare a List<ICanSync>
and call Synchronize()
on all of them.
Controller<TableA> Controller1 = new Controller<TableA>(new TableA());
Controller<TableB> Controller2 = new Controller<TableB>(new TableB());
Controller<TableC> Controller3 = new Controller<TableC>(new TableC());
List<ICanSync> ControllerList = new List<ICanSync> {Controller1, Controller2, Controller3};
foreach (var controller in ControllerList)
{
controller.Synchronize();
}
This is type safe, because ICanSync
is naturally compatible with instances of itself. If you need richer common functionality that uses T
, you can declare a covariant or contravariant interface.