0

You can box and unbox types like this:

List<object> items = new List<object>();
items.Add("hello");
items.Add(123);

Here, it doesn't matter what we put in, as long as it derives from object. So this works.

But is it possible to do it with generic classes? Like this:

public class Foo<T>
{
    public T MyItem;
}

static void Main()
{
    List<Foo<object>> items = new List<Foo<object>>();
    items.Add(new Foo<string>() { MyItem = "Hello" });
    items.Add(new Foo<int>() { MyItem = 123 });
}

Here, it will give me an error despite string and int is type of object.

There is one easy solution that i have thought about, and that is by turning new Foo< string > into new Foo< object >, and then just put in string value in the object type like this:

items.Add(new Foo<object>() { MyItem = "Hello" });

But i'm in a situation where i cant do that.

So is there any solution to make this possible?

Assassinbeast
  • 1,207
  • 1
  • 17
  • 33
  • Might be similar to this question https://stackoverflow.com/questions/4403055/boxing-and-unboxing-with-generics – aggaton Nov 06 '17 at 20:24
  • 4
    It’s not “turning string into object”. Boxing is wrapping value types in an object so they can be treated as reference types. That’s happening when you add the int. The string is a reference type already. What you’ve encountered here is variance: `Foo` isn’t `Foo`. Nothing to do with boxing. It’s important to learn the basics of the type system. – 15ee8f99-57ff-4f92-890c-b56153 Nov 06 '17 at 20:25
  • 3
    Covariance and Contravariance always gives me a headache so I'm not going to give an answer but I'd say have a Google of those terms and it should shed some light – Dave Nov 06 '17 at 20:27
  • 1
    Understand [Covariance and Contravariance](https://learn.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance) – Vikhram Nov 06 '17 at 20:28
  • You can't substitute `Foo` or `Foo` for `Foo`, they are all types so you can't add them to the `List>` instance. To make that clearer what you are *trying* to do with your lists is essentially this: `Foo item = new Foo();`. – Igor Nov 06 '17 at 20:28
  • Okay, just edited it to explain it a little better now – Assassinbeast Nov 06 '17 at 20:28
  • @Assassinbeast It’s very well explained already. You simply can’t do that, for reasons you’ve been given. If you explain what problem you hope to solve with this code, we can probably help you find a solution that works. – 15ee8f99-57ff-4f92-890c-b56153 Nov 06 '17 at 20:36
  • 2
    To solve that problem you have to be clear why exactly you need this. What operations are you going to perform on that list? – Evk Nov 06 '17 at 20:48

2 Answers2

3

Given this class:

public class Foo<T>
{
    public T MyItem { get; set; }
}

If you have a list of Foo<object> you can't add a Foo<int> to it. If you could, then you could do this:

var myListOfFoos = new List<Foo<object>>();
myListOfFoos.Add(new Foo<int>());
Foo<object> firstFoo = myListOfFoos[0]; // this is the Foo<int> you added.
firstFoo.MyItem = "string!"; // How can you do this with a Foo<int>?

My eyes always glaze over from the words "covariant" and "contravariant." The compiler is always protecting you from a scenario like this. You just have to figure out what it's (correctly) protecting you from trying to do.

Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
3

To attempt to make your code compile:

You would have to make the parameter T covariant which can only be done on interfaces and delegates. The other limiting factor when defining T as covariant is that T cannot be a value type.

Documentation - out (Generic Modifier) (C# Reference)

For generic type parameters, the out keyword specifies that the type parameter is covariant. You can use the out keyword in generic interfaces and delegates.

... Covariance and contravariance are supported for reference types, but they are not supported for value types.


The question about boxing/unboxing also does not apply in this situation with generics. Generics ensure there never is boxing or unboxing (by default) on the type parameters. There are plenty of good links in the comments, I recommend you read through them so you have a better understanding of 1) boxing/unboxing and 2) generics.


This is the closest you can get with your code

static void Main() {
  List<IFoo<object>> items = new List<IFoo<object>>();
  items.Add(new Foo<string>() { MyItem = "Hello" });


  // not possible because int is a value type
  // items.Add(new Foo<int>() { MyItem = 123 }); 
}

interface IFoo<out T>
{
    T MyItem {get;}
}
class Foo<T> : IFoo<T>
{
    public T MyItem {get;set;}
}
Igor
  • 60,821
  • 10
  • 100
  • 175