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

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!

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

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

Related

Convert object of type List<object> to List<string>

I'm parsing a JSON string using MiniJson in Unity. Now I have a value that's a list of strings. Though I get this an object. The underlying type of the object is a List<object>, but actually it should be a List<string>. So currently I do this:
void SetInfo(Dictionary<string,object> info) {
Colors = (info["colors"] as List<object>).OfType<string>().ToList();
}
The dictionary is what I get from the MiniJson library. This works perfectly. Though seems a bit ugly, a lot of unnecessary casting, and I was wondering if there's a better way of doing this. Mostly I'm looking for cleaner code, and faster execution, since there are a lot of these entries.
Edit: I wasn't very clear about this. In the info dictionary there are a bunch of key/value pairs, the type of the values vary. I can cast most very easily, but the info["colors"] object is the one I'm having problems with.
So when I do info["colors"] I get an object, with an underlying type List<object>. The objects in this list are actually strings.
Also, there's not really a performance problem here, since it's only called once at the start of the program, though there is a noticeable lag currently, I'm going to put it on it's own thread anyway so no problem there. The faster execution is just out of curiosity.
Try this one.
(info["colors"] as List<object>).ConvertAll(input => input.ToString());
you can also use direct cast (string)input or input as string which is even faster(there were debates on casting here at stackoverflow).
info["colors"] as List<object> can result in null, so I'd rather be more safe and wrap your code like this:
List<object> colors = info["colors"] as List<object>;
List<string> typedColors = null;
if (colors!=null)
{
typedColors = colors.ConvertAll(input => input as string);
}
Have you thought about using the in and out keywords
I know this code is a bit old school, but we all know what it means:
List<object> l = new List<object>();
List<string> s = new List<string>();
foreach( var i in l )
s.Add((string)i);

Fetching values from an object with nested objects

I'm using a service where I don't know the actual structure of the response. Therefore I'm returning it as an object:
object result = Service.GetStuff();
If I inspect it in the debugger, it looks something like this:
I've tried casting it to dynamic, Hashtable and Arraylist, without success. How do I access the properties on the object? Similar SO questions haven't helped me.
Cannot apply indexing with [] to an expression of type `object'
If the result is always an array (or something that implements IList, like an ArrayList), you can cast it to an IList, which is where the index operator is defined:
IList result = Service.GetStuff() as IList;
Then you can apply the index operator. If it's not always an array then you could use reflection to determine if it is an array, then cast it.
The bigger question is, what are you going to do with the objects since you don't know what they are?
It seems that it is some sort of dictionary try casting the object to IDictionary. If that does not help, you may call the result.GetType() to see what is the actual type and cast to that.
I also think that dynamic should work too.
dynamic result = Service.GetStuff();
int reconnectDelay = ((dynamic)result[0]).reconnectDelay;

Convert IEnumerable<A> to IEnumerable<B> without knowing types at compile time

I have two types: let's call them A and B. A can be converted to B using an adapter method.
I then have a collection of A's in a List<A> (it could be any collection type that supports IEnumerable<A>).
I now want to convert from IEnumerable<A> to IEnumerable<B>. I know the Type of each of A and B, and I have a method to convert an A into a B, but my method and/or class is not templated itself, so I do not have access to the template type; e.g. the T in IEnumerable<T>.
I effectively want to write this ConvertCollection method, where I know "from" is of type IEnumerable<{something}>:
object ConvertCollection(object from, Type fromType, Type toType, Converter converter);
My converter looks like this:
delegate object Converter(object from);
My attempt leaves me here:
object ConvertCollection(object from, Type fromType, Type toType, Converter converter)
{
return ((IEnumerable<object>)from).Select(converter);
}
which partly works. If I call it like this
ConvertCollection(new List<A>() { new A() }, typeof(A), typeof(B), AToBConverter);
the returned collection does contain a collection of Bs, but the collection itself is of type IEnumerable<object>, not IEnumerable<B>, because I don't know how to cast to IEnumerable<{toType}>. (It matters because the result needs to be serialized).
I can attack it from the other end and create the correct return type like this:
var result = Activator.CreateInstance(typeof(List<>).MakeGenericType(toType));
// TODO: populate result here
return result;
but then the problem is that to achieve the TODO part, I need to call List<> methods on result, but I can't cast it to any type of List<> because of Co/ContraVariance rules, so even though I know the type supports List<> methods, I can't get at them to use them to populate the list; e.g. to use Add().
Is there a way to do this without using 'dynamic' and without too much reflection? I know I could locate and invoke the Add() method via reflection, but it seems like it shouldn't be necessary.
.NET 4.0 BTW
-- Clarification
As Euphoric correctly speculates, and I tried but rather badly failed to convey above, I know the types A and B at runtime, but I do not know them at compile time. Hence the direct use of generics is not an option. I do know that the collections (both supplied and as must be returned) implement the generic IEnumerable<>. That is all fixed and outside my control. (I've adjusted the title accordingly).
** Edit 2: fixed some formatting causing <> to not display (easy to accidentally omit the back-ticks!)
Using the LINQ Select method:
var result = listA.Select(a => Converter(a));
Since you are using .NET 4.0, you really should avoid using object and use generics.
The solution I settled on was to use reflection to invoke the Enumerable.Cast<> method to cast the resultant collection from IEnumerable<object> to the correct IEnumerable<> type. I got the idea from the answer to this question: Convert IEnumerable to IEnumerable<T> when T isn't known until runtime. Seems to involve very little performance penalty.
So the full answer becomes:
object ConvertCollection(object from, Type fromType, Type toType, Converter converter)
{
var partialResult = ((IEnumerable<object>)from).Select(converter);
var castMethod = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(toType);
return castMethod.Invoke(null, new[] { partialResult });
}
Maybe something like this?
IEnumerable<TTo> ConvertCollection<TFrom,TTo>(object from, Converter converter)
{
return ((IEnumerable<TFrom>)from).Select(a=>(TTo)converter(a)).ToList();
}
Then you simply call it:
ConvertCollection<A,B>(new List<A>() { new A() }, AToBConverter);

Dynamically setting type or result from invoked generic method?

My apologies if the question is somewhat unclear; I'm not entirely certain how to phrase this.
My issue is this. I have two classes, Manager<T> and Result<T>. Within Manager<T>, I have a whole raft of retrieval functions. Ordinarily, I would call Manager<T> and set its type like so:
Manager<SpecialDataType> mgr = new Manager<SpecialDataType>;
After which I set up my Result type, and fill it with my function from Manager, where 1 is a parameter for the GetItem function shown. I can then access things in my item:
Result<SpecialDataType> item = new Result<SpecialDataType>;
item = mgr.GetItem(1);
string x = item.Teaser;
OK. So now, what I'm trying to do is set the <SpecificDataType> to be filled in at run time. I think I've got half of the solution already, using generic types, like so:
Type generalType= Type.GetType("SpecificDataType");
Type managerType= typeof(Manager<>).MakeGenericType(generalType);
var managerInstance= Activator.CreateInstance(managerType);
object[] args = {1};
MethodInfo getItemMethod = managerInstance.GetMethod("GetItem");
But here's where I get stuck. There are specific properties that my Result class has that I need to be able to access. They are, or course, set by the data type I'm casting into. When I do an Invoke, like so:
var item = getItemMethod.Invoke(managerInstance, args);
I'm not getting any of my properties that I know are part of item. That makes sense, I suppose, because we don't know what item is. So, we tried this:
Type dataType = typeof(SmartFormData<>).MakeGenericType(sfType);
var item = Activator.CreateInstance(dataType);
item = getItemMethod.Invoke(managerInstance, args);
And got the same result. I can't seem to get to item.Teaser.
I'm not a c# coder natively (as though that's not apparent already from this overly complicated question I'm asking), so I'm not incredibly familiar with reflection and generic types. Can anyone point me in the right direction for how to solve this problem, or how to approach it from a different angle? The only caveat is that I cannot modify the Manager<T> and Result<T> functions; I have to use what I'm given there.
Thanks in advance for any help you can offer.
As Dark Falcon correctly notes in his comment you will have to use reflection to get the members of your item.
Or, if you are in .NET 4 or above, you can use the dynamic keyword to greatly simplify things:
Type generalType= Type.GetType("SpecificDataType");
Type managerType= typeof(Manager<>).MakeGenericType(generalType);
dynamic managerInstance = Activator.CreateInstance(managerType);
var item = managerInstance.GetItem(1);
string x = item.Teaser;
You need to cast the invocation result to the type you're expecting
var item = (Result<SpecificDataType>)getItemMethod.Invoke(managerInstance, args);

Add a List<object> or Arraylist to an array of CustomObject[]

I have tried many ways like
Cast<CustomObject>, as Customobject and ToArray(Customobject) but nothing worked.
How can I add List or ArrayList via AddRange to a CustomObject[] Array?
Code is really difficult.
But if you have some time you can get the complete source of the destination list from here:
http://www.codeproject.com/Articles/4012/C-List-View-v1-3?msg=3844172#xx3844172xx
This is a Custom Listview
I activated a combobox for the second column, so I can select diferrent values for a cell.
But before this, I have to add something to select.
This is the hole problem.
Update:
Firstly, thanks for the help !
Secondly, Found a solution in the comments from the website with the source.
Had to add some code and changed the destination custom array to a List
list.Cast<CustomObject>().ToArray()
Will work as long as the things in the list are actually CustomObject. If they might be other types, you can use OfType<CustomObject>() instead of Cast. This will filter out anything of an incompatible type.
Assuming the objects really are instances of CustomObject, use LINQ Select method:
objList.Select(o => o as CustomObject).ToArray();
Otherwise you will get an array of null.
If its a List<CustomObject> then let us say
CustomObject[] coarr = list_of_customobject.ToArray();
If its an ArrayList then
CustomObject[] coarr = arraylist.OfType<CustomObject>().ToArray();
If you are unsure whether all of your objects are of the type CustomObject try
var result = list.OfType<CustomObject>.ToArray();
Strictly speaking you cannot add elements to an array, since an array's length remains constant over its lifetime. There are two things you can do:
Create a new array
myArray = myTList.ToArray() // generic)
myArray = myArrayList.Cast<CustomObject>().ToArray() // cast, non-generic
myArray = myArrayList.OfType<CustomObject>().ToArray() // filter by type, non-generic
Set elements of an array
myArray[x] = myTList[y] // generic
myArray[x] = (CustomObject)myArrayList[y] // non-generic
I recommend you to take the generic collection whenever possible. They provide you additional type safety. Casting object variables cause runtime errors you could detect at compile time by using generic types.
If you actually want to add elements to an existing collection, you may try to use a dynamic collection type rather than an array: List<T> : IList<T> or LinkedList<T> : ICollection<T> are a good point to start, or maybe more specific types like Stack<T> or Queue<T>.

Categories