0

A discussion came up recently at work where we were trying to discern which of the following would be better.

We have a base type for our domain, with many classes inheriting from it:

public abstract class DomainBase
{
    public int BaseId { get; set; }
}

In another spot in code, we have a method that takes in a set of objects and does work based on BaseId. It doesn't return the objects back or otherwise use the type given at all.

My question: Is it better to use a generic here, or just take in the base types?

Option A: void DoWork<TDomainBase>(List<TDomainBase> objects) where TDomainBase : DomainBase;

Option B: void DoWork(List<DomainBase> objects);

In our code these are called the same, as we never work with List<DomainBase> that have different types i the collection (e.g. new List<DomainBase> { new Foo(), new Bar() }). I want to say that using the generic is better, so that we don't implicitly convert objects to their base class when passing them into the method. But I want to make sure I'm not missing something obvious here. (Or, heck, C# might just optimize all this away under the hood regardless of which option I choose!) Happy to be proven wrong.

Edited to remove incorrect statement.

Rand Random
  • 7,300
  • 10
  • 40
  • 88
Doctor Blue
  • 3,769
  • 6
  • 40
  • 63
  • 6
    "Functionally they behave the same way" - no they don't. You can't pass a `List` into the second option. The non-generic form won't be actually converting any objects though. – Jon Skeet Jan 29 '18 at 13:18
  • @JonSkeet You're right! For all intents and purposes in our code, though, we don't have `List` that contains different kinds of `DomainObject` in them. – Doctor Blue Jan 29 '18 at 13:19
  • We need to know much more about your project and how you use this method etc. Even then still it'll be an opinion-based question. – Fabjan Jan 29 '18 at 13:31
  • @Fabjan Not sure how much more I can share. Both options would work. This question is more geared to the language in general, as opposed to something more specific to our application. Given a base object constraint, should a void method just take in a list of the base, or is it better to use a constrained type generic? I'm OK with opinions. That's basically the reason I posted it. :) – Doctor Blue Jan 29 '18 at 13:33
  • 2
    @Scott: No-one suggested that the list would have to have *different* kinds of domain objects in them. My point is that it's fairly natural to create a `List` for a list with all the *same* type of domain object in - and you can't pass that into option B, as a `List` isn't convertible to a `List`. – Jon Skeet Jan 29 '18 at 13:47
  • @JonSkeet ah I see now. Yeah that makes the choice kind of obvious then. The presumption that you can pass `List` in to a method expecting `List` was wrong. – Doctor Blue Jan 29 '18 at 13:55

1 Answers1

2

"Both options should work" - Well, that's almost correct.

Using the generic option your can simply pass a list of your base type or any type that derives from it to the method directly:

void DoWork<TDomainBase>(List<TDomainBase> objects) where TDomainBase : DomainBase
...
...
var list = new List<SomeClassDerivedFromDomainBase>() {/* initialize items */};

DoWork(list);

However, using a list of base class, you will have to do one extra step and generate that list from your list of derived types:

void DoWork(List<DomainBase> objects);

var list = new List<SomeClassDerivedFromDomainBase>() {/* initialize items */};

DoWork(list); // This will cause a compilation error:
// Cannot implicitly convert type 'System.Collections.Generic.List<SomeClassDerivedFromDomainBase>' to 'System.Collections.Generic.List<DomainBase>'

The reason you will get the compilation error is explained in the answers to Why can't I assign a List<Derived> to a List<Base>?

To work around it, you will have to do something like this:

DoWork(list.ToList<DomainBase>());

I think it should be pretty clear by now that the generic option is better in this case.

Zohar Peled
  • 79,642
  • 10
  • 69
  • 121