Search code examples
c#inheritancecovariance

Define base class method working on derived class collection


I'm trying to put a generic Add method in my base class that will work on different types of classes, all implementing ICollection. So far so good, I'm able to use boxing/unboxing to achieve what I want, but I wonder if there is a better way to do this. I've played little with covariant interface, but without greater luck - is it even possible to define IVariantCollection?

Here is a pice of code that should explain what I try to achieve:

public abstract class Device
{
    public string Name { get; set; }
    public abstract void Print();
}

public class Printer : Device { public override void Print() => Debug.WriteLine($"{Name} printer printout"); }

public class Xero : Device { public override void Print() => Debug.WriteLine($"{Name} xero printout."); }

public abstract class Factory
{
    public abstract IEnumerable<Device> DeviceCollection { get; }
    public abstract void Add(object added);
    public void ListDevices() { foreach (var item in DeviceCollection) Debug.WriteLine($"Device: {item.Name}"); }
}

public class PrinterFactory : Factory
{
    public List<Printer> Printers = new List<Printer>();
    public override IEnumerable<Device> DeviceCollection => Printers;

    public override void Add(object added) { Printers.Add((Printer)added); }
}

public class XeroFactory : Factory
{
    public ObservableCollection<Xero> Xeros = new ObservableCollection<Xero>();
    public override IEnumerable<Device> DeviceCollection => Xeros;

    public XeroFactory() { Xeros.CollectionChanged += (s, e) => Debug.WriteLine($"Device added: {e.NewItems[0]}"); }
    public override void Add(object added) { Xeros.Add((Xero)added); }
}

The code works, but I somehow don't like this solution with object - is there other way to define Add method, maybe a generic one in base class?


Solution

  • Use generic Factory with base class constraint

    public abstract class Factory<TDevice> where TDevice : Device
    {
        public abstract IEnumerable<TDevice> DeviceCollection { get; }
        public abstract void Add(TDevice added);
        public void ListDevices() 
            { 
                foreach (var item in DeviceCollection) 
                    Debug.WriteLine($"Device: {item.Name}"); 
            }
    }
    

    Then

    public class PrinterFactory : Factory<Printer>
    {
        public List<Printer> Printers = new List<Printer>();
        public override IEnumerable<Printer> DeviceCollection => Printers;
    
        public override void Add(Printer added) { Printers.Add(added); }
    }