How to cast from an object[] stored as object in C# - c#

I have a List<object> stored in a database.
Each list object consists of an object[] consisting of int values.
I can save, view, and retrieve the data. I can view the data in the debugger. But I cannot cast back to int or an array.
foreach (object item in list)
{
if (item.GetType().IsArray)
{
var arr = item as int[];
foreach (var i in arr)
print(i);
}
}
At the if statement, item shows the data in the debugger pictured below but is false, but how do I cast back to object[]?
I have also tried:
var newItem = item as object[];
Edit: This is how I'm initializing the object. I start with an object because I get cast errors if I try wrapping an int[] when I send to the database.
var listValues = new List<object>();
var newArray = new object[10];
newArray[0] = (int)c.Tag;
newArray[1] = (int)c.FPos;
newArray[2] = (int)c.ToL;
listValues.Add(newArray);

A cast is (usually) different from a conversion. Most of the time when you're casting things in C#, you assume those things are already what you say they are, and you're not changing them at all. There is an exception for value types like int that get "boxed" and "unboxed" when you cast them to and from object. However, that exception does not extend to casting an object[] into an int[].
An int[] is not the same thing as an object[], so you can't just cast it as one. Instead, you have to produce a new array (or Collection, or IEnumerable, or whatever) that consists of all of those objects unboxed into ints. One way to do this is to use the Cast<>() extension method from the System.Linq namespace.
int[] arr = ((object[])item).Cast<int>().ToArray();
Or, as a more complete example:
List<object[]> list = new List<object[]> { new object[] { 1, 2 }, new object[] { 3, 4 } };
foreach (object[] item in list)
{
if (item.GetType().IsArray)
{
var arr = item.Cast<int>();
foreach (var i in arr)
{
Console.WriteLine(i);
}
}
}
Update
Based on your updated question, chances are that the real solution to your problem will go way beyond the scope of the original question. I don't know what mechanism you're using to store this and retrieve it from the database, but if you're using something like Entity Framework you probably need to change your model so that its values are strongly typed. In fact, the way you're taking properties off of an object and putting them into the database as an array is a big code smell: most likely your data model should be flattened into a type with named properties.
But to answer the simplest, most basic part of the question: you've got to cast your objects to the type that they actually are before trying to convert them. If you've got a List<object>, then use that:
foreach (List<object> item in list)
{
int[] arr = item.Cast<int>().ToArray();
foreach (var i in arr)
{
Console.WriteLine(i);
}
}

After selecting your items from the DB you don't get a real array. Instead you get a List which cannot simply be cast to an array type. Thus item.GetType().IsArray is also false because it's a List
Try the following:
foreach (object item in list)
{
IEnumerable<object> itemAsObjectEnumerable = (IEnumerable<object>)item;
IEnumerable<int> itemAsIntEnumerable = itemAsObjectEnumerable.Cast<int>();
foreach (var i in itemAsIntEnumerable)
{
print(i);
}
}

Related

How to iterate thru an array without knowing the underlying type in C#

Let's say I have a collection of objects, some of which are arrays.
I would like to concat all the values to print them, but I don't know the type of elements in the arrays.
foreach(var item in myList)
{
var val = item.Property;
if (val.GetType().IsArray)
{
var array = val as IEnumerable<object>;
val = string.Join(",", array);
}
DoSomething(val);
}
If val contains a string[], this code snippet will work, also for a myClass[].
But if it contains a int[] or double[], then array will be null, meaning the dynamic cast failed.
If int or double are inherited from System.TypeValue, which inherits from object, why don't this code snippet work?
And how could I achieve that?
Edit: changed the code snippet to be more explicit and avoid the wrong variable usage that did showup because I wrongly simplified my code.
It's not allowed to do so in C#, more details here.
But you can cast to non-generic IEnumerable, and then cast everything to object before pushing to string.Join():
foreach(var val in myList)
{
if (val.GetType().IsArray)
{
var array = (IEnumerable)val;
// It's not allowed to set val variable, but I assume it's just an example
val = string.Join(",", array.Cast<object>());
}
}
If you're just worried about arrays in general, or better yet IEnumerable (as per your cast) then I suggest leaving the reflective portion of checks out and simply attempting to convert to IEnumerable.
foreach (var val in myList)
{
if (val is IEnumerable enumeration)
{
string.Join(",", array); //You can't use val (as per your example because it's the element of the loop)
}
}
I honestly don't know what you're intending to do with the array, as your adding it to a string as an object, which will just be array, array, array... and so on pretty much. I'm going to post a better solution however I'm not sure if it's what you want or not because what you're doing above doesn't make a lot of sense.
var notSureWhyStringBuilder = new StringBuilder();
foreach (var val in myList)
{
if (val is IEnumerable enumeration)
{
notSureWhyStringBuilder.Append($",{enumeration.ToString()}");
}
}
Console.WriteLine(notSureWhyStringBuilder.ToString());
Now with this at least I feel like you're more in the direction that you want to be in but that doesn't sit well with me because I don't know what you're going to gain out of it.
I'm going to post one more example, one that will iterate and build the inner enumeration into the string for you. I don't know or assume that's what you want but between the 3 examples I'm providing hopefully you can take away and re-engineer it to get what you need and possibly learn from it.
var notSureWhyStringBuilder = new StringBuilder();
foreach (var val in myList)
{
if (val is IEnumerable enumeration)
{
foreach (var innerEnumeration in enumeration)
{
notSureWhyStringBuilder.Append($",{innerEnumeration.ToString()}");
}
}
}
Console.WriteLine(notSureWhyStringBuilder.ToString());
Here's a small console app that I put together for you to piddle with. Copy and paste it as is and it should work.
Console App
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Question_Answer_Console_App
{
class Program
{
private const string ShortTab = " ";
private static readonly List<object> ListOfObjects = new List<object>()
{
4,
"Michael",
new object(),
new Program(),
69.4,
new List<string>() {"Mathew", "Mark", "Luke", "John" },
new int[] { 1, 3, 5, 7, 9 }
};
static void Main(string[] args)
{
var itemsText = new StringBuilder();
var arrayCounter = 0;
foreach (var item in ListOfObjects)
{
if (item is IEnumerable enumeration)
{
itemsText.AppendLine($"Array: {++arrayCounter}");
if (item is string text)
{
itemsText.AppendLine($"{ShortTab}{text}");
}
else
{
foreach (var innerItem in enumeration) itemsText.AppendLine($"{ShortTab}{innerItem.ToString()}");
}
}
}
Console.WriteLine(itemsText.ToString());
Console.Read();
}
}
}
Outputs
Array: 1
Michael
Array: 2
Mathew
Mark
Luke
John
Array: 3
1
3
5
7
9
First off, trying to assign to val inside the foreach loop will not work. You can't change a collection you are iterating over.
So you need to build up a new collection. Something like this works, see how the yield return statement in the iterator lets you build up a new IEnumerable of the leaf objects and works for both objects and value types at any level.
class Program
{
static void Main(string[] args)
{
var myWackyList = new object[] {
new[]{1d, 2d},
3d,
new[]{4d, 5d},
new []
{
new[]
{
6d
}
},
"7",
new[]{ "8", "9"}
};
Console.WriteLine( string.Join(", ", Flatten( myWackyList )));
}
static IEnumerable<object> Flatten(IEnumerable enumerable)
{
foreach (var val in enumerable)
{
if ( val.GetType().IsArray )
foreach ( var flattenedVal in Flatten( val as IEnumerable ) )
yield return flattenedVal;
else
yield return val;
}
}
}
You cannot cast a value-type array to an IEnumerable because variance only applies if there's no representational conversion needed. Therefore it doesn't apply for value types. In this particular case this conversion would mean boxing.
I would do it this way to prevent unnecessary boxing, like in the case with Cast, for example.
public static string Stringify(this object o, string delimiter)
{
if (!(o is IEnumerable enumerable))
return o.ToString();
var sb = new StringBuilder();
foreach (var i in enumerable)
{
if (sb.Length > 0)
sb.Append(delimiter);
sb.Append(i.ToString());
}
return sb.ToString();
}

Array, List, IEnumerable, CustomList class cast to one and iterate threw them

I am trying to figure out a way that tells me if a certain type is an array/list/ienumerable/collection ... I dont care what kind of it is even CustomLists so something like
FooList<T> : IList<T>
FooList : IList
or stuff like that.
I kinda hoped that a simple type.IsArray would be enough but sadly this isnt the case.
I need a way to check if its one of the above types and then check what the underlying type is, and than cast it to a Indexed based collection, where I can loop through the entries.
For a simple array this is all I need:
if (obj.GetType().IsArray)
{
var elementType = obj.GetType().GetElementType();
if (elementType.IsPrimitive == false)
{
var array = (Array)obj;
}
}
This should work for every collection, there could possible be.
Edit:
As recommended below, I should as/is to IEnumerable but with IEnumerable I have the problem that the I cannot set certain object inside this IEnumerable.
With array I have used the method array.SetValue(obj, index) which works fine.
When I loop threw the IEnumerable and try to set one entry like this:
var list = obj as IEnumarble;
if (list != null)
{
foreach (var item in list)
{
item = new object();
}
}
I am getting the following message:
Readonly local variable cannot be used as an assignment target.
You can try to cast it with the as operator:
var enumerable = list as IEnumerable;
if (enumerable != null)
{
foreach (object item in enumerable)
{
// ...
}
}
However, if you need to modify it you have to recreate it. For example by using a list which you fill in the loop. Then reassign it to the original variable.
Or you could check if the type is a ILIst in the first place (like an array or list), then you can use it`s indexer:
var ilist = list as IList;
if (ilist != null)
{
for (int i = 0; i < ilist.Count; i++)
{
ilist[i] = "new value";
}
}
IIRC, you could do a simple inheritence check for the enumerable interface via
if (FooList is IEnumerable)
// We have a List
You can also use Linq and do a
if (FooList.ToList().Count > 1)
// We have a List
But this would be rather unconventional.

how to convert a genericlist to an object array -> object[]

I googled and fined this link, but still not working
Convert List<T> to object[]
I want to convert the list of int to object array. Why? because I want to add the list as object array in Combbox, it's the argument.
The problem is it adds just "one item" Object[] Array in Combobox, while "tempList" contains 4 items of type int.
I like to add the 4 items in the object[] (object array), now it adds as 1 item and just shows Object[] array in the debugger.
When I look in debugger and type :
customers - it shows object[1] and when I type
customers[0] it shows object[4], so in fact 4 items are added, but how can I get these values.???
List<int> tempList= new CustomersRepository().GetAll().Select(a => a.Numero).ToList();
object[] customers = new object[] { tempList.Cast<object>().ToArray() };
ComboBox.Items.AddRange(customers);
What you are doing is currently creating an Array of arrays. So accessing the values would be done by the following :
customers[0][1]
What I suspect you are actually looking for is the following :
object[] customers = tempList.Cast<object>().ToArray();
This will create an Array of object items called customers.
List<int> tempList= ...;
object[] customers = tempList.Cast<Object>().ToArray();
object[] customers = new object[] { tempList.Cast<object>().ToArray() };
Here you create an object[] with one item: another object[] containing the items of tempList.
Just use object[] customers = tempList.Cast<Object>().ToArray() instead of wrapping it in another object[].
Try it this way :
var customers = tempList.Cast<object>().ToArray();
Or also with an explicit cast :
var customers = tempList.Select(t => (object)t).ToArray();
The problem occured because you are using an initializer for building your list.
This syntax :
var arr = new object[] { "a", "b" }
initialize an array with two strings.
So when you are writing
var arr = new object[] { tempList.Cast<object>().ToArray() }
you build an array of one array!
You dont need ToList() if you anyway want to process further and obtain an array later.
Something like this should be more efficient:
var temp = new CustomersRepository().GetAll().Select(a => a.Numero);
object[] customers = temp.Cast<object>().ToArray();
ComboBox.Items.AddRange(customers);
Had temp been a collection of reference types, you need not cast at all, but rely on array covariance. This should work:
var temp = new CustomersRepository().GetAll().Select(a => a.StringProperty);
object[] customers = temp.ToArray(); //no need of Cast<object>
ComboBox.Items.AddRange(customers);
But this doesn't work in your case since array covariance doesnt support for value types.
Another idea is to have an extension method AddRange that accepts any IEnumerable, not just object[]. Something like:
public static void AddRange(this IList list, IEnumerable lstObject)
{
foreach (T t in lstObject)
list.Add(t);
}
Now you can call:
var customers = new CustomersRepository().GetAll().Select(a => a.Numero);
ComboBox.Items.AddRange(customers);
The best of all is to add the Customers as such, and set the DisplayMember or ValueMember properties to your property, like Numero. Since you have the entire objects in combobox you have more info with you. Something like:
ComboBox.DisplayMember = "Numero";
var customers = new CustomersRepository().GetAll();
ComboBox.Items.AddRange(customers); // using the above extension method

Convert an object array of object arrays to a two dimensional array of object

I have a third party library returning an object array of object arrays that I can stuff into an object[]:
object[] arr = myLib.GetData(...);
The resulting array consists of object[] entries, so you can think of the return value as some kind of recordset with the outer array representing the rows and the inner arrays containing the field values where some fields might not be filled (a jagged array). To access the individual fields I have to cast like:
int i = (int) ((object[])arr[row])[col];//access a field containing an int
Now as I'm lazy I want to access the elements like this:
int i = (int) arr[row][col];
To do this I use the following Linq query:
object[] result = myLib.GetData(...);
object[][] arr = result.Select(o => (object[])o ).ToArray();
I tried using a simple cast like object[][] arr = (object[][])result; but that fails with a runtime error.
Now, my questions:
Is there a simpler way of doing this? I have the feeling that some
nifty cast should do the trick?
Also I am worried about performance
as I have to reshape a lot of data just to save me some casting, so I
wonder if this is really worth it?
EDIT:
Thank you all for the speedy answers.
#James: I like your answer wrapping up the culprit in a new class, but the drawback is that I always have to do the Linq wrapping when taking in the source array and the indexer needs both row and col values int i = (int) arr[row, col]; (I need to get a complete row as well like object[] row = arr[row];, sorry didn't post that in the beginning).
#Sergiu Mindras: Like James, i feel the extension method a bit dangerous as it would apply to all object[] variables.
#Nair: I chose your answer for my implementation, as it does not need using the Linq wrapper and I can access both individual fields using int i = (int) arr[row][col]; or an entire row using object[] row = arr[row];
#quetzalcoatl and #Abe Heidebrecht: Thanks for the hints on Cast<>().
Conclusion: I wish I could choose both James' and Nair's answer, but as I stated above, Nair's solution gives me (I think) the best flexibility and performance.
I added a function that will 'flatten' the internal array using the above Linq statement because I have other functions that need to be fed with such a structure.
Here is how I (roughly) implemented it (taken from Nair's solution:
public class CustomArray
{
private object[] data;
public CustomArray(object[] arr)
{
data = arr;
}
//get a row of the data
public object[] this[int index]
{ get { return (object[]) data[index]; } }
//get a field from the data
public object this[int row, int col]
{ get { return ((object[])data[row])[col]; } }
//get the array as 'real' 2D - Array
public object[][] Data2D()
{//this could be cached in case it is accessed more than once
return data.Select(o => (object[])o ).ToArray()
}
static void Main()
{
var ca = new CustomArray(new object[] {
new object[] {1,2,3,4,5 },
new object[] {1,2,3,4 },
new object[] {1,2 } });
var row = ca[1]; //gets a full row
int i = (int) ca[2,1]; //gets a field
int j = (int) ca[2][1]; //gets me the same field
object[][] arr = ca.Data2D(); //gets the complete array as 2D-array
}
}
So - again - thank you all! It always is a real pleasure and enlightenment to use this site.
You could create a wrapper class to hide the ugly casting e.g.
public class DataWrapper
{
private readonly object[][] data;
public DataWrapper(object[] data)
{
this.data = data.Select(o => (object[])o ).ToArray();
}
public object this[int row, int col]
{
get { return this.data[row][col]; }
}
}
Usage
var data = new DataWrapper(myLib.GetData(...));
int i = (int)data[row, col];
There is also the opportunity to make the wrapper generic e.g. DataWrapper<int>, however, I wasn't sure if your data collection would be all of the same type, returning object keeps it generic enough for you to decide what data type cast is needed.
There are few similar answer posted which does something similar. This differ only if you want to acess like
int i = (int) arr[row][col];
To demonstrate the idea
public class CustomArray
{
private object[] _arr;
public CustomArray(object[] arr)
{
_arr = arr;
}
public object[] this[int index]
{
get
{
// This indexer is very simple, and just returns or sets
// the corresponding element from the internal array.
return (object[]) _arr[index];
}
}
static void Main()
{
var c = new CustomArray(new object[] { new object[] {1,2,3,4,5 }, new object[] {1,2,3,4 }, new object[] {1,2 } });
var a =(int) c[1][2]; //here a will be 4 as you asked.
}
}
(1) This probably could be done in short and easy form with dynamic keyword, but you'll use compile-time checking. But considering that you use object[], that's a small price:
dynamic results = obj.GetData();
object something = results[0][1];
I've not checked it with a compiler though.
(2) instead of Select(o => (type)o) there's a dedicated Cast<> function:
var tmp = items.Select(o => (object[])o).ToArray();
var tmp = items.Cast<object[]>().ToArray();
They are almost the same. I'd guess that Cast is a bit faster, but again, I've not checked that.
(3) Yes, reshaping in that way will affect the performance somewhat, depending mostly on the amount of items. The impact will be the larger the more elements you have. That's mostly related to .ToArray as it will enumerate all the items and it will make an additional array. Consider this:
var results = ((object[])obj.GetData()).Cast<object[]>();
The 'results' here are of type IEnumerable<object[]> and the difference is that it will be enumerated lazily, so the extra iteration over all elements is gone, the temporary extra array is gone, and also the overhead is minimal - similar to manual casting of every element, which you'd do anyways.. But - you lose the ability to index over the topmost array. You can loop/foreach over it, but you cannot index/[123] it.
EDIT:
The James's wrapper way is probably the best in terms of overall performance. I like it the most for readability, but that's personal opinion. Others may like LINQ more. But, I like it. I'd suggest James' wrapper.
You could use extension method:
static int getValue(this object[] arr, int col, int row)
{
return (int) ((object[])arr[row])[col];
}
And retrieve by
int requestedValue = arr.getValue(col, row);
No idea for arr[int x][int y] syntax.
EDIT
Thanks James for your observation
You can use a nullable int so you don't get an exception when casting.
So, the method will become:
static int? getIntValue(this object[] arr, int col, int row)
{
try
{
int? returnVal = ((object[])arr[row])[col] as int;
return returnVal;
}
catch(){ return null; }
}
And can be retrieved by
int? requestedValue = arr.getIntValue(col, row);
This way you get a nullable object and all encountered exceptions force return null
You can use LINQ Cast operator instead of Select...
object[][] arr = result.Cast<object[]>().ToArray()
This is a little less verbose, but should be nearly identical performance wise. Another way is to do it manually:
object[][] arr = new object[result.Length][];
for (int i = 0; i < arr.Length; ++i)
arr[i] = (object[])result[i];

C# Accessing data in System.Object[]

I'm coding in C# and I have a dictionary with tons of data. One of the members is "children" but when I try to write out its values, I get:
System.Object[]
I know that children contains data, probably nested data, but I'm unsure if it's a list, dictionary, array, etc.
How do I write out all the data within "children"?
The default response of any instantiated .NET type to "ToString()" is to write out the fully qualified type name.
System.Object[] means that you have an array where each element is of type "Object". This "box" could contain anything since every type in .NET derives from Object. The following may tell you what the array really contains in terms of instantiated types:
foreach (object o in children)
Console.WriteLine(o != null ? o.GetType().FullName : "null");
It it an array of object references, so you will need to iterate over it and extract the objects, For example:
// could also use IEnumerable or IEnumerable<object> in
// place of object[] here
object[] arr = (object[])foo["children"];
foreach(object bar in arr) {
Console.WriteLine(bar);
}
If you know what the objects are, you can cast etc - or you can use the LINQ OfType/Cast extension methods:
foreach(string s in arr.OfType<string>()) { // just the strings
Console.WriteLine(s);
}
Or you can test each object:
foreach(object obj in arr) { // just the strings
if(obj is int) {
int i = (int) obj;
//...
}
// or with "as"
string s = obj as string;
if(s != null) {
// do something with s
}
}
Beyond that, you are going to have to add more detail...
(Note I didn't test this code in VS, working off memory here).
object[] children = (object[])foo["children"];
foreach(object child in children)
System.Diagnostics.Debug.WriteLine(child.GetType().FullName);
This should dump out the classnames of the children.
If you are doing a foreach on foo["children"] you shouldn't be getting a failure that a public iterator isn't found since by definition an array has one (unless I missed something).
I realize that this thread is over a year old, but I wanted to post a solution I found in case anyone is wrestling with trying to get data out of the System.Object[] returned using the Cook Computing XML-RPC Library.
Once you have the Children object returned, use the following code to view the key/values contained within:
foreach (XmlRpcStruct rpcstruct in Children)
{
foreach (DictionaryEntry de in rpcstruct)
{
Console.WriteLine("Key = {0}, Value = {1}", de.Key, de.Value);
}
Console.WriteLine();
}
"I know that children contains data, probably nested data, but I'm unsure if it's a list, dictionary, array, etc"
So the childreen is IEnumerable or not a collection
try this code
void Iterate(object children)
{
if(children is IEnumerable data)
foreach(object item in data)
Iterate(item);
else Console.WriteLine(children.ToString());
}

Categories