Zoltán
Zoltán

Reputation: 184

Casting object to List<(Enum, string)> issue

I am creating a converter by implementing IMultiValueConverter with *Convert(object[] values, Type targetType, object parameter, CultureInfo culture)

I am passing a List<(SomeEnumType, string)> tuple.

via MultiBinding and on the converter side I would like to cast but it throws a casting error.

I tried : var result = (List<(Enum, string)>)values[1];

but I got this casting issue:

'Unable to cast object of type 'System.Collections.Generic.List1[System.ValueTuple2[Vasco.Basics.Contracts.CoreConfigurations.Enums.ApplicationType,System.String]]' to type 'System.Collections.Generic.List1[System.ValueTuple2[System.Enum,System.String]]'.'

It is strange because If I pass only one element of SomeEnumType and try to case like (Enum)values[1] casting works well.

When I pass a List<SomeEnumType> and try to cast like (List<Enum>)values[1] does not work already.

Thank you in advance!

Upvotes: 0

Views: 903

Answers (2)

bfren
bfren

Reputation: 493

In general you cannot cast Lists like this - because what you are actually trying to do is cast each item in the list, rather than the list itself. Therefore you'd need to loop through and cast each item individually, like so:

var input = new List<(SomeEnumType, string)>();
// now add items to the input list

var result = new List<(Enum, string)>();
foreach (var element in input)
{
    result.Add(
        ((Enum)element.Item1, element.Item2)
    );
}

Remember, a tuple is not a single element but a wrapper for multiple elements, which each need casting.

Or, you could use a tool that allows you to 'map' types, e.g. Mapster or AutoMapper - I personally prefer Mapster.

using Mapster;

var input = new List<(SomeEnumType, string)>();
// now add items to the input list

var result = input.Adapt<List<(Enum, string)>>();
// Adapt<>() is an extension method provided by Mapster

Upvotes: 0

DekuDesu
DekuDesu

Reputation: 2292

When I pass a List and try to cast like (List)values1 does not work already.

You generally aren't allowed to cast generic collections like List<T> or IEnumerable<T> to other types. This comes down to how C# and the compiler handle generics and something called Covariance and contravariance. This is an incredible complicated topic, at least for me, so I won't bogg you down with the fine details.

Consider the following situation.

List<string> strings = new() { "Kitten", "Mouse", "horse" };
List<object> objs = strings;

This may seem pretty natural, especially if you try to explicitly cast the strings list such as (List<object>)strings, but this wont compile and that is a good thing! It protects you from doing silly things, like for example:

List<string> strings = new() { "Kitten", "Mouse", "horse" };
List<object> objs = strings;
objs.Add(1.29d);

this may seem like it's only tangentially related to you question, but this is really important, and is the exact reason you cant cast a collection to a different kind of collection, even if you know that they're very similar.

When we add that double to the objs list (assuming that this would compile, it doesn't), what were doing effectively is adding a double to a List<string> which would break everything about how strongly typed languages such as C# work.

It is strange because If I pass only one element of SomeEnumType and try to case like (Enum)values1 casting works well.

The reason you can do this, but not collections, is becuase with a single object the compiler can check to see if there is a valid conversion and do the conversion for you manually. Unlike with collections where the compiler, if it did the same thing as it did with single objects, it would add things to collections that may not match the type that was constrained when that collection was initialized.

Credit to John Skeet for this explanation, Ch4.4.1 ISBN 9781617294532

Upvotes: 2

Related Questions