Maxzeth
Maxzeth

Reputation: 23

Putting an object implied to be of a less derived type into a list of a more derived type

In this situation, I have a list of the more derived type and another of the less derived type. In order to avoid arbitrary clutter in the less derived list I wrote a foreach statement that checked each element in the list if it was of the more derived type, and if so, move it to that element to the more derived list. This is demonstrated here:

namespace Test
{
    class Tester
    {
        static void Method()
        {
            List<A> ListA = new List<A>();
            List<B> ListB = new List<B>();
            //populate the lists
            foreach (A a in ListA)
                if (a.GetType().ToString()=="Test.B")
                {
                    ListA.Remove(a);
                    ListB.Add(a);
                }
            //Do stuff with the lists
        }
    }
    class A
    {
        //stuff in class A
    }
    class B : A
    {
        //stuff in class B
    }
}

This gives me a compiler error saying that an object of type A cannot be implicitly converted to type B since the compiler isn't smart enough to realize that the instance of a that would be converted tested positive for being of type B so there should not be a conversion in the first place. Is there a way to circumvent this issue?

Upvotes: 2

Views: 86

Answers (1)

Jon Skeet
Jon Skeet

Reputation: 1500225

Is there a way to circumvent this issue?

Yes - you cast it:

foreach (A a in listA)
{
    if (a.GetType().ToString() == "Test.B")
    {
        listA.Remove(a);
        listB.Add((B) a);
    }
}

That will compile, but it's still going to fail, because you're modifying the collection you're iterating over. The simplest way to fix this is to iterate over a copy of the list instead:

foreach (A a in listA.ToList())

There are various other alternatives, depending on your requirements.

Note that it would be cleaner to use is or as rather than comparing type names. I'd also strongly recommend that you get into the habit of using braces for all foreach statements, even ones with only a single statement in the body, and follow common naming conventions for local variables (listA instead of ListA for example).

Also note that this isn't a matter of the compiler "not being smart enough" - it's a matter of the compiler following the rules of the C# language specification. The compile-time type of a is A - it's as simple as that. Any other code that happens to be in the loop doesn't change that compile-time type, and therefore doesn't change the ways in which you can use it.

While occasionally this may be annoying, it makes the language significantly simpler and more predictable. It would be really weird if previous operations changed how overload resolution worked, etc.

Upvotes: 5

Related Questions