How to pass List<DerivedClass> when param type is List<BaseClass>?
Asked Answered
I

3

17

How can i pass a list which is a list of DerivedObjects where the Method is expecting a list of BaseObjects. I am converting the list .ToList<BaseClass>() and am wondering if there is a better way. My second problem is the syntax is incorrect. I am trying to pass the list byref and i am getting an error: 'ref' argument is not classified as a variable

How can I fix these two problem? thanks.

public class BaseClass { }
public class DerivedClass : BaseClass { }

class Program
{
    static void Main(string[] args)
    {
        List<DerivedClass> myDerivedList = new List<DerivedClass>();
        PassList(ref myDerivedList.ToList<BaseClass>());
// SYNTAX ERROR ABOVE IS - 'ref' argument is not classified as a variable

        Console.WriteLine(myDerivedList.Count);
    }

    public static void PassList(ref List<BaseClass> myList)
    {
        myList.Add(new DerivedClass());
        Console.WriteLine(myList.Count);
    }
}

Solved:

A method similar to this has solved my issue.

public static void PassList<T>(ref List<T> myList) where T : BaseClass
{
    if (myList == null) myList = new List<T>(); 
    // sorry, i know i left this out of the above example.

    var x = Activator.CreateInstance(typeof(T), new object[] {}) as T;
    myList.Add(x);
    Console.WriteLine(myList.Count);
}

Thank you to all who help across this question and from other SO questions.

Incisor answered 4/10, 2011 at 4:12 Comment(5)
Why do you need ref in this case?Divorcee
ref is required otherwise the added items remains boxed with the method. (hope i got those terms right).Incisor
List is a reference type class. The items added in the method will be added to the same instance.Divorcee
@Valamas: No, you don't understand how ref works (or possibly how reference types work). Follow the link in my answer and read it really carefully.Gib
I had not seen the "where T : BaseClass" syntax before, which is useful. Thanks for posting the solved code.Haight
G
21

The ref part is easy: to pass an argument by reference, it has to be a variable, basically. So you can write:

List<BaseClass> tmp = myDerivedList.ToList<BaseClass>();
PassList(ref tmp);

... but that won't affect either the value of myDerivedList or contents itself.

The ref here is pointless anyway, as you're never changing the value of myList within the method anyway. It's important to understand the difference between changing the value of a parameter and changing the contents of the object that the parameter value refers to. See my article on parameter passing for more details.

Now as for why you can't pass your list in - it's to preserve type safety. Suppose you could do this, and we could write:

List<OtherDerivedClass> list = new List<OtherDerivedClass>();
PassList(list);

Now that would be trying to add an instance of DerivedClass to a List<OtherDerivedClass> - that's like adding an apple to a bunch of bananas... it doesn't work! The C# compiler is preventing you from performing that unsafe operation - it won't let you treat a bunch of bananas as a fruit bowl. Suppose we did have Fruit instead of BaseClass, and Banana / Apple as two derived classes, with PassList adding an Apple to the list it's given:

// This is fine - you can add an apple to a fruit bowl with no problems
List<Fruit> fruitBowl = new List<Fruit>();
PassList(fruitBowl);

// This wouldn't compile because the compiler doesn't "know" that in PassList
// you're only actually adding an apple.
List<Apple> bagOfApples = new List<Apple>();
PassList(bagOfApples);    

// This is the dangerous situation, where you'd be trying to really violate
// type safety, inserting a non-Banana into a bunch of bananas. But the compiler
// can't tell the difference between this and the previous one, based only on
// the fact that you're trying to convert a List<Banana or Apple> to List<Fruit>
List<Banana> bunchOfBananas = new List<Banana>();
PassList(bunchOfBananas );    

C# 4 allows generic variance in certain situations - but it wouldn't help in this particular case, as you're doing something fundamentally unsafe. Generic variance is a fairly complicated topic though - as you're still learning about how parameter passing works, I would strongly suggest that you leave it alone for the moment, until you're more confident with the rest of the language.

Gib answered 4/10, 2011 at 4:19 Comment(3)
Cheers Jon for the explanation and link to your website. I have solved my problem using generics.Incisor
Can I ask what List<BaseClass> tmp = myDerivedList.ToList<BaseClass>(); does exactly? Does it create a copy or what? I have a similar need, and--to my surprise--if I do this and pass the result to my method, changes made to the objects are reflected in the original list. (I am modifying the objects, not adding or removing anything from the list.)Rufina
@JonathanWood: It creates a shallow copy of the list. So changes to the original list in terms of additions and removals will not be seen in the copy, but changes to the objects will - because the new list contains references to the same objects.Gib
D
5

If you must keep the ref, you can do this:

var list = myDerivedList.ToList<BaseClass>();
PassList(ref list);
// SYNTAX ERROR ABOVE IS - 'ref' argument is not classified as a variable

For a ref parameter, it needs a variable to reference. When created on-the-fly there's nothing to reference later in code so it really makes no sense.

Distemper answered 4/10, 2011 at 4:18 Comment(0)
L
0

First, in the code you provided you don't need the ref because you don't really change the destination (reference) myList in any way. Then of course you get an error with the ref myDerivedList.ToList<..>() because with a byref you can change where a variable is pointed to, but you don't give a variable, you give a value (so just read the compiler complain ;) )

To you point: read about Co- and Contravariance - if you are using .net 4.0

Logsdon answered 4/10, 2011 at 4:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.