Visar inlägg med etikett generics. Visa alla inlägg
Visar inlägg med etikett generics. Visa alla inlägg

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)));
 
        }
 
    }
}