onsdag 25 juni 2008

Converting generic lists of concrete class to lists of implemented interface


Have you noticed that if you fill a generic List with a concrete class that implements an interface, the generic List does not think it is filled with objects of that interface? And you can't cast the list to a List of the interface.


Let's look at a simple example where we have an IPerson interface and a concrete class, Person, implementing IPerson:


namespace ConvertingGenericLists
{
    public interface IPerson
    {
        string Name { get; set; }
    }
    public class Person : IPerson
    {
        public string Name { get; set; }
    }
}

Nothing strange. However, if we create a List<Person> it is not a List<IPerson> and the compiler will not tolerate a cast like (List<IPerson>).

However, every element in the list is both of type Person and IPerson, but the list itself is of Person.

The neat trick is to use the ConvertAll()-method in List and with a simple lambda-method convert it.

Like this (PersonList is a List<Person>):

IPersonList = PersonList.ConvertAll(c => (IPerson) c);


Now the lists contents is that of the interface, but as you will see in the code below, every element is still of IPerson and Person.


And here is a complete example using NUnit tests:


using System.Collections.Generic;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
 
namespace ConvertingGenericLists
{
    [TestFixture]
    public class Tests
    {
        private List<Person> PersonList;
        private List<IPerson> IPersonList;
 
        [TestFixtureSetUp]
        public void Setup()
        {
            PersonList = new List<Person>
                             {
                                 new Person {Name = "Aurel"},
                                 new Person {Name = "Thomas"},
                                 new Person {Name = "Lasse"}
                             };
 
            //And the magic of ConvertAll() and lamdba:
            IPersonList = PersonList.ConvertAll(c => (IPerson) c);
        }
        [Test]
        public void PersonList_Is_Of_Base_Type_Only()
        {
            Assert.That(PersonList, Is.InstanceOfType(typeof(List<Person>)));
            Assert.That(PersonList, Is.InstanceOfType(typeof(IList<Person>)));
            Assert.That(PersonList, Is.InstanceOfType(typeof(IEnumerable<Person>)));
            //An element in PersonList is indeed of type IPerson:
            Assert.That(PersonList[0], Is.InstanceOfType(typeof(IPerson)));
 
            //But PersonList is not a list of IPersons!
            Assert.That(PersonList, Is.Not.InstanceOfType(typeof(List<IPerson>)));
        }
        [Test]
        public void IPersonList_Is_A_List_Of_IPerson()
        {
            Assert.That(IPersonList, Is.InstanceOfType(typeof(IList<IPerson>)));
            Assert.That(IPersonList, Is.InstanceOfType(typeof(List<IPerson>)));
            Assert.That(IPersonList, Is.InstanceOfType(typeof(IEnumerable<IPerson>)));
 
            //IPersonlist is not of the implementing type anymore:
            Assert.That(IPersonList, Is.Not.InstanceOfType(typeof(List<Person>)));
            Assert.That(IPersonList, Is.Not.InstanceOfType(typeof(IList<Person>)));
            Assert.That(IPersonList, Is.Not.InstanceOfType(typeof(IEnumerable<Person>)));
 
            //But an element in IPersonList is STILL a Person and a IPerson!
            Assert.That(IPersonList[0], Is.InstanceOfType(typeof(Person)));
            Assert.That(IPersonList[0], Is.InstanceOfType(typeof(IPerson)));
 
        }
 
    }
}