none
Casting a 'List Of' a derived class to a 'List of' the parent class RRS feed

  • Question

  • I am programming in VB2015 (.net 4.6)

    I define a Class A and a Class B that inherits from A. I also have a method that expects a List(of A) as a parameter.

    I was surprised to realize that I am not able to call the  method with a List(of B) as an argument.  I assume it is because the class List(of B) does not derive from the class List(of A). Is it the correct explanation ?

    It is frustrating since logically a List(of B) is really a List(of A). I can come up wiht work around but is there a "clean" solution ?

     --------------------

        Class A
        End Class

        Class B
            Inherits A
        End Class

        Sub Foo(Data As List(Of A))
        End Sub

         Dim L As List(Of B)
          Foo(L)    <--------------------  This call not correct becuase L  is not of type List(Of A)



    • Edited by Ybaca Tuesday, February 23, 2016 12:16 AM Spelling
    Tuesday, February 23, 2016 12:14 AM

Answers

  • If you need just to enumerate the data (using For Each or LINQ), then consider this too:

        Sub Foo(Data As IEnumerable(Of A))

            ...

        End Sub

    The List(Of B) will be automatically passed to IEnumerable(Of A).

    See also: Covariance and Contravariance (https://msdn.microsoft.com/en-us/library/ee207183.aspx).

    • Proposed as answer by cheong00Editor Tuesday, February 23, 2016 8:52 AM
    • Marked as answer by Ybaca Saturday, February 27, 2016 1:50 PM
    • Unmarked as answer by Ybaca Saturday, February 27, 2016 1:50 PM
    • Marked as answer by Ybaca Saturday, February 27, 2016 1:51 PM
    Tuesday, February 23, 2016 7:17 AM

All replies

  • List(Of T).ConvertAll() comes to rescue.

        Sub Foo(Data As List(Of A))
        
        End Sub
    
        Dim L As New List(Of B)
        Foo(L.ConvertAll(new Converter(Of B, A)(AddressOf BToA)))
    
        Public Function BToA(ByVal input As B) _
            As A
    
            Return DirectCast(input, A)
        End Function

    EDIT: Of course using Linq will do too:

    Foo((From bitem In L Select DirectCast(bitem, A)).ToList())

    Note that as I primarily do programming in C#, the above syntax is not verified and you might have to fix them yourself.
    • Edited by cheong00Editor Tuesday, February 23, 2016 1:47 AM
    • Proposed as answer by Blackwood Tuesday, February 23, 2016 4:04 AM
    Tuesday, February 23, 2016 1:37 AM
    Answerer
  • Thanks a lot, unfortunately, it is not working.

    I tried to the following but it does not work:

    Dim Input as List(Of B)

    Dim output as List(of A) = Directcast(Input, List(of A))

    The error message says that List(of B) cannot be converted to List(of A)

    The description of Directcast mentions that "Directcast requires an inheritance or implementation relationship between the data types of the two argument.

    If you have something that works in C# please provide it as is, I am familiar with C++

    Tuesday, February 23, 2016 3:12 AM
  • Thanks a lot, unfortunately, it is not working.

    I tried to the following but it does not work:

    Dim Input as List(Of B)

    Dim output as List(of A) = Directcast(Input, List(of A))

    The error message says that List(of B) cannot be converted to List(of A)

    The description of Directcast mentions that "Directcast requires an inheritance or implementation relationship between the data types of the two argument.

    If you have something that works in C# please provide it as is, I am familiar with C++

    This code doesn't match the answer given by cheong00.
    Tuesday, February 23, 2016 4:06 AM
  • You need to Cast the elements themselves too.

    In C#, it would be something like the following:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        public class A
        {
        }
    
        public class B : A
        {
        }
    
        class GenericConvert
        {
            private void Foo(List<A> data)
            {
            }
    
            public void Run()
            {
                List<B> L = new List<B>();
                this.Foo(L.ConvertAll(x => (A)x));
            }
    
            public void Run2()
            {
                List<B> L = new List<B>();
                this.Foo(L.Select(x => (A)x).ToList());
            }
        }
    }

    Tuesday, February 23, 2016 6:16 AM
    Answerer
  • If you need just to enumerate the data (using For Each or LINQ), then consider this too:

        Sub Foo(Data As IEnumerable(Of A))

            ...

        End Sub

    The List(Of B) will be automatically passed to IEnumerable(Of A).

    See also: Covariance and Contravariance (https://msdn.microsoft.com/en-us/library/ee207183.aspx).

    • Proposed as answer by cheong00Editor Tuesday, February 23, 2016 8:52 AM
    • Marked as answer by Ybaca Saturday, February 27, 2016 1:50 PM
    • Unmarked as answer by Ybaca Saturday, February 27, 2016 1:50 PM
    • Marked as answer by Ybaca Saturday, February 27, 2016 1:51 PM
    Tuesday, February 23, 2016 7:17 AM
  • Thanks for looking into this again. What you suggest would work. My understanding is that ConvertAll creates a new list with each item casted to the new type (correct ?). In this specific case it would be equivalent to walking though the items of List (of B) and accumulating the itmes in a List(of A). No casting is actually necessary at the level of the items since B inherits from A and a B can be added to a List(of A). For example this code does the job very simply.

    Dim LoA as new List(of A)(LoB)  (where LoB is the list of B)

    However I was hoping to find a syntactic solution, e.g. one that does not create any computational burden. I do understand that in the VB class framework List(of B) is not a derived type from List(of A) but I don’t see  how the programming logic would break down if it were possible to cast a List(of B) into a List(Of a) – without making a copy.

    One practically reason why I was hopoing for a syntactic solution is because the function I am calling modifies the list and later the calling code accesses its elements assuming that they are of Class B.   That means that if I make a copy one way, I will need to make a copy back into a List (of B) upon return or cast the element every time the code accesses them.

    At the end the best solution I found so far in my specific situation is to avoid creating a List(of B) in the first place, instead I will use a List(of A), which can be sent directly to the function. But every time the calling codes accesses the list, I will have to cast the individual elements back to the B class type. It will make the code a little cumbersome but there will be no computational burden as the casting is purely formal in this case. 


    • Edited by Ybaca Wednesday, February 24, 2016 4:41 AM adding detail
    Wednesday, February 24, 2016 4:13 AM
  • Thanks a lot for your suggestion. In this case, the called function does modify the list and I need the list structure. However, i will use your suggestion elsewhere as it makes the code more versatile.
    Wednesday, February 24, 2016 4:17 AM
  • To the benefit of anyone running into the same issue, i want to highlight that Viorel's answer is actually the basis of the solution.  Using IEnumerable(Of A) as a parameter type allows to generalize the type in case the only thing the function needs to do is walking though the list but there is also the option of defining the function parameter as ICollection(of A) or even as IList(Of A) for a richer set of feature. 

    After all this is a remind that the interfaces is VB's wsy to address multiple inheritance.

    Saturday, February 27, 2016 1:58 PM