Daniel Brown Daniel Brown - 29 days ago 12
C# Question

What is the best practice for setting/determining the concrete types in a List<interface> or List<abstractClass>?

If I have either:

List<iCanSpeak>
or
List<Animal>


Where many types implement the interface
iCanSpeak
or use the abstract class
Animal
as a base class.

What is the best practice for determining/setting the concrete types that are in my actual list?

I know C# offers
.TypeOf
and I have seen examples that use an enumeration in the base class or interface to make determining easier, but what is the best practice and why?

Potentially off-topic follow-ups:

Also, what is
TypeOf
doing under the hood? Is it up casting? Why aren't the properties of the different concrete classes lost when put into a
list<abstractClass>
?

Does...
List<AbstractClass> == List<interface>


If the defined methods and properties are the same?

Answer

You can use the .OfType<TSomeInterfaceOrClassOrAbstractClassOrStruct>() extension method to select all list members that are of type iCanSpeak or inherit from it.

In your case this could be: MyList.OfType<ICanSpeak>().

Some references: DotNetPearls and MSDN.

Does... List<AbstractClass> == List<interface>

If the defined methods and properties are the same?

No. They dont. Since lists are a generic class, you cant just convert them to each other. You will need a cast/convert function to do that for you.

This can be .OfType<T> that filters, or .Cast<T> that returns a new IEnumerable of that type, or .Select(x => (yournewtype)x) that also returns a IEnumerable of yournewtype. I would prefer .Cast<T> (this method is for converting lets say a list of implementation to a list of IInterfaceThatIsImplemented) if possible, else .OfType<T> if you dont need all members, but only the ones with a certain type.

Example:

var cars = new List<Car>();
var vehicles = cars.Cast<IVehicle>(); // works
var carsAndBicycles = new List<IVehicle>(); // think of some cars and bicycles in here
var otherCars = carsAndBicycles.OfType<Car>(); // works
var broken = carsAndBicycles.Cast<Car>(); // this breaks when carsAndBicycles doesnt only contain cars 
// this is what .OfType<T> is useful for.

To go even more in depth - what I wrote above isnt very accurate.

(Source: Jon Skeets Edulinq Implementation/Blog)

List<AbstractClass> and List<interfaceThatIsImplementedInAbstractClass> are the same.

Not in C# kindof, the compiler doesn't allow it, but the CLR allows it.

If the checks from the compiler are circumvented, it works.

(I can not recommend using that "hack" in any sane code!)

int[] ints = new int[10];
// Fails with CS0030
IEnumerable<uint> uints = (IEnumerable<uint>) ints;

// Succeeds at execution time
IEnumerable<uint> uints = (IEnumerable<uint>)(object) ints;

Lets have some more fun:

int[] ints = new int[10];

if (ints is IEnumerable<uint>)
{
    Console.WriteLine("This won’t be printed");
}
if (((object) ints) is IEnumerable<uint>)
{
    Console.WriteLine("This will be printed");
}