C
C#2y ago
lukkasz323

❔ ✅ I can't convert List<BaseClass> to List<T> where T: BaseClass.

I have a List<List<Component>>. Component is a base class, and each item in this outer list is actually a List with different sub-classes of Component. At run-time I expect it to look like this:
List<List<Component>>:
List<SubComponentA>
List<SubComponentB>
List<SubComponentC>
List<List<Component>>:
List<SubComponentA>
List<SubComponentB>
List<SubComponentC>
Everything works nicely besides this - I can't find a way to get for example List<SubComponentA> from the outer list. Here's my current attempt:
32 Replies
lukkasz323
lukkasz323OP2y ago
Visual Studio propses this fix:
lukkasz323
lukkasz323OP2y ago
Which gets rid of the error, but still throws an exception at runtime
lukkasz323
lukkasz323OP2y ago
vdvman1
vdvman12y ago
You can't do that because it would allow a List<SubClass> to be converted to a List<BaseClass> and then an instance of BaseClass added to the list, which is invalid since the List<SubClass> is expecting all instances to be a SubClass However, if you just want to loop over the list, you can cast it to an IEnumerable<T> instead of a List<T>
lukkasz323
lukkasz323OP2y ago
I would like to be able to use .Add()
lukkasz323
lukkasz323OP2y ago
vdvman1
vdvman12y ago
Then you need to make sure that when you originally create the list that it is of the correct type, and store it as just an IList in the outer list Also, using a dictionary is probably better than a list here
lukkasz323
lukkasz323OP2y ago
btw this still throws an exception, unless you meant something else?
lukkasz323
lukkasz323OP2y ago
Yeah but then I wouldn't be able to use generics I think Here's how the method is used
lukkasz323
lukkasz323OP2y ago
vdvman1
vdvman12y ago
private Dictionary<Type, IList> _componentCollections = new();

public List<T> GetComponentCollection<T>()
where T : Component
{
var type = typeof(T);
if (!_componentCollections.TryGetValue(type, out IList? untypedList))
{
untypedList = new List<T>();
_componentCollections[type] = untypedList;
}

return (List<T>)untypedList;
}
private Dictionary<Type, IList> _componentCollections = new();

public List<T> GetComponentCollection<T>()
where T : Component
{
var type = typeof(T);
if (!_componentCollections.TryGetValue(type, out IList? untypedList))
{
untypedList = new List<T>();
_componentCollections[type] = untypedList;
}

return (List<T>)untypedList;
}
lukkasz323
lukkasz323OP2y ago
so Types as a key? I did that before switching to generics
vdvman1
vdvman12y ago
Yup, using the type as the key, and making sure that the new List<T>() has the correct type Unfortunately C# doesn't have a way to do this completely type safely, but as long as you implement your methods correctly it should work fine
lukkasz323
lukkasz323OP2y ago
okay thank you I'm wondering if im not overengineering this, all I actually need is - a few collections for each subclass of Component, each collection auto-generated for a different subcomponent. Then a way to get these collections by passing Type or preferably generic T to Read and Add to them. Everything besides "Get collection" and "Add to collection" is private too.
lukkasz323
lukkasz323OP2y ago
I ended up with something like this, I'd appreciate anything that can be done to simplify or improve this.
lukkasz323
lukkasz323OP2y ago
_componentCollections is a Dictionary btw
jalepi
jalepi2y ago
Covariance and Contravariance in Generics
Learn about covariance, which allows you to use a more derived type, and contravariance, which allows you to use a less derived type, in .NET generics.
vdvman1
vdvman12y ago
That wouldn't let the caller add to that list
lukkasz323
lukkasz323OP2y ago
Now I see something. Am I right that this code doesn't preserve the same object reference? I think that's a problem, I wonder if there is a way to preserve that. I think this doesn't work, no matter what I try
vdvman1
vdvman12y ago
Can you show what you tried?
lukkasz323
lukkasz323OP2y ago
yeah 1 minute the problem is that it's a List<BaseClass> to List<T> conversion.
vdvman1
vdvman12y ago
That shouldn't be happening with the code I showed
lukkasz323
lukkasz323OP2y ago
you have IList right? Is that a IList or IList<>?
vdvman1
vdvman12y ago
The dictionary stores IList yes, but the new List that adds to the dictionary uses the actual type
lukkasz323
lukkasz323OP2y ago
I assumed before that you meant the generic IList<>, i'll try this instead. thanks, this works so far. Are there any downsides to this?
jalepi
jalepi2y ago
Why don't you use one list containing a mix of all components?
static void Main(string[] args)
{
var components = new List<Component>();
components.Add(new SubComponentA());
components.Add(new SubComponentA());
components.Add(new SubComponentB());
components.Add(new SubComponentC());

var subComponentAs = components.OfType<SubComponentA>().ToList();
Console.WriteLine($"SubComponentA count: {subComponentAs.Count}");
}

abstract class Component { }
class SubComponentA : Component { }
class SubComponentB : Component { }
class SubComponentC : Component { }
static void Main(string[] args)
{
var components = new List<Component>();
components.Add(new SubComponentA());
components.Add(new SubComponentA());
components.Add(new SubComponentB());
components.Add(new SubComponentC());

var subComponentAs = components.OfType<SubComponentA>().ToList();
Console.WriteLine($"SubComponentA count: {subComponentAs.Count}");
}

abstract class Component { }
class SubComponentA : Component { }
class SubComponentB : Component { }
class SubComponentC : Component { }
lukkasz323
lukkasz323OP2y ago
I think there was one problem here, i'll test this ToList(), isn't that getting rid of the original reference?
jalepi
jalepi2y ago
.ToList() returns a new list, with the same references inside.
lukkasz323
lukkasz323OP2y ago
yeah references inside are the same but I wanted to be able to use .Add() on that list so now changes are only preserved in one of the lists i think
jalepi
jalepi2y ago
not sure exactly what your use case is about, why having a list of lists? you could use an object with different lists as its properties.
lukkasz323
lukkasz323OP2y ago
Something like this. Yeah at this moment I'm sure a simple object would be better, but originally I didn't think it'd be this complicated. I just wanted a way to auto-add all components with reflection. I guess this could be marked as solved as I have a few different ways to do this now
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server