4

I have three classes, two of which inherit from a base class, and the third which I would like to reference one of the other two depending on the state of the application.

public class Batch
{        
    public Batch() { }
}

public class RequestBatch : Batch
{
    public RequestBatch(string batchJobType) : base(batchJobType) { }

    public override int RecordCount
    {
        get { return Lines.Count; }
    }
}

public class ResponseBatch : Batch
{       
    public ResponseBatch(string batchJobType) : base(batchJobType) { }

    public ResponseBatch(int BatchJobRunID)
    { }
}

Sometimes I have an instance of Child1 instantiated, and sometimes I need Child2. However, I have model that I want to pass around my application to keep everything in one place, but I want a way to make the property that holds Child1 and Child2 generic, for example:

public class BatchJob {
   public List<Batch> Batches { get; set; }
}

And then later do this

public List<RequestBatch> GetBatches(...) {}

var BatchJob = new BatchJob();
BatchJob.Batches = GetBatches(...);

However, the compiler yells at me saying it can't implicitly convert Child1 to (its base type) Parent.

I get red squiggles under "= GetBatches(...." saying "Cannot implicitly convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.List'

Is there a way to generify the Property so it can take any abstract of type Parent?

Thanks!

  • 10
    This should work. Can you show the real code? – Yacoub Massad Feb 17 '16 at 19:26
  • 6
    That should not happen; classes are implicitly convertible to their base types. Please show a complete example. – SLaks Feb 17 '16 at 19:26
  • 6
    Give a *complete* and *minimal* example, and *state the error message*. A question like this is basically "something is broken" -- we don't know what. – Eric Lippert Feb 17 '16 at 19:29
  • 3
    Why do people keep upvoting this question? – Eldar Dordzhiev Feb 17 '16 at 19:30
  • 2
    @EldarDordzhiev Because it was his first question and he described it fairly well, even if the code is incomplete. I upvoted because I'm tired of seeing really bad quality questions being posted all day. – Arian Motamedi Feb 17 '16 at 19:31
  • Do you have the right assemblies and references in place? – visc Feb 17 '16 at 19:31
  • I bet its because you are creating you properties in a class where you did not include your reference to parent. Namespace or using error – visc Feb 17 '16 at 19:33
  • 1
    I up voted because someone was complaining :-D – visc Feb 17 '16 at 19:34
  • 1
    https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx – Matthew Whited Feb 17 '16 at 19:49
  • 1
    http://ericlippert.com/category/covariance-and-contravariance/ – Matthew Whited Feb 17 '16 at 19:54
  • 1
    @MatthewWhited That fixed it! The compiler error was distracting me. I thought it couldn't convert RequestBatch to Batch, but it couldn't convert a List to List I changed the Batches property to type IEnumerable and it worked. I even tried that once, not sure what I had done differently the first time. Thanks! – FlockOfTanks Feb 17 '16 at 19:55
  • Yeah... I thought about writing it up as an answer but it has been don't tons of times here on SO. – Matthew Whited Feb 17 '16 at 19:56
  • Possible duplicate of [Convert List to List](http://stackoverflow.com/questions/1817300/convert-listderivedclass-to-listbaseclass) – Matthew Whited Feb 17 '16 at 20:02
  • You could use an interface for the common properties of your model classes and make the use the interface as the type of your RunningJob property. – edesr Feb 17 '16 at 20:50
  • @JeffreyHaines Ok, that makes sense. – Eldar Dordzhiev Feb 17 '16 at 21:03
  • 2
    Now that you've clarified the question: this question is asked a lot here. A list of turtles cannot be used as a list of animals because you can put a tiger into a list of animals. A list of animals cannot be used as a list of turtles because it might contain a tiger. Therefore a list of animals and a list of turtles are totally incompatible. But a list of turtles *can* be used as a **sequence** of animals, because there's no way to put a tiger into a sequence. IEnumerable has no "add" method. – Eric Lippert Feb 17 '16 at 23:44

2 Answers2

1

The code snipped you show does work. There is no compiler error:

class Program
{
    static void Main()
    {
        var rj = new RunningJob();
        rj.Property = new Child1();
        rj.Property = new Child2();
    }
}
public class RunningJob { 
    public Parent Property { get; set; }
}
public class Parent {    }
public class Child1 : Parent {    }
public class Child2 : Parent {    }

The only issue that comes with this code is that Property is of type Parent. So you cannot call methods that are specific for Child1/Child2. This can be done using constraints on generic type parameters on class RunningJob :

public class RunningJob<TParent> where TParent : Parent
{
    public TParent Property { get; set; }
}

Hence, now it is ensured that Property is of type Parent or any derived types.

Michael Mairegger
  • 6,833
  • 28
  • 41
0

One option...

public new IEnumerable<RequestBatch> GetBatches(...) {
    get 
    {
        return base.GetBatches(...).OfType<RequestBatch>();
    }
}

Another...

If you don't need to modify the collection then just change from List<T> to IEnumerable<T>

More Info...

Matthew Whited
  • 22,160
  • 4
  • 52
  • 69
  • 1
    @OP: If it is considered an error for `base.GetBatches` contain anything other than instances of `RequestBatch`, you may wish to replace `OfType` with `Cast` so that such an error is detectable. – Brian Feb 18 '16 at 17:50