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());
}
Related
I am trying to recursively build a list of all the fields of an object that are of a certain type. Recursion is needed because the object can hold arrays of objects which, in turn, may contain fields of a certain type.
So, if an object has a field of type Wanted, I want that object in the list.
class a
{
Wanted m_var;
};
If an object has an array that contains other objects and these objects contain a field of type Wanted, I'd also like those added to the list.
class b
{
int m_whatever;
a[] m_vararray;
};
I've been trying all sorts of approaches, including Reflection but I am not getting anywhere. All the attempts were some variation of this
private IList<object> ListOfTypeWanted(object fields)
{
IList<object> result = new List<object>();
System.Reflection.MemberInfo info = fields.GetType();
object[] properties = type.GetProperties();
foreach (var p in properties)
{
if (true == p.GetType().IsArray)
{
result.Add(ListOfTypeWanted(p));
}
else
{
Type t = p.GetType();
if (p is WantedType)
{
result.Add(this);
}
}
}
return result;
}
I've also tried Linq statements using Where() but they did not get me any useful results either.
Does anyone know what the best way is to achieve this?
Your question contains many typo and syntax error, but I can advise few:
Make sure you want GetProperties not GetFields. Your example seems like you only have fields, not properties.
Make sure you are providing proper BindingFlags when calling GetProperties. Your example seems like containing private member. In that case BindingFlags.NonPublic | BindingFlags.Instance should be used.
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);
}
}
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.
I am aware that C# does not deal with pointers but I wonder if I can get a ref to a list when I know its first element?
For example:
Let's say I have a list defined as
List<T> abc
If I have abc[0], can I get a reference of abc?
I am new to C#, I apologize if my question seems weird. In C/C++, I can get the address of an array abc by using &abc[0]. Does C# provide us with similar tool that help us refer back to the collection itself when we know one item in the collection?
Thanks,
This is now possible, starting with .NET 5.0, by using the System.Runtime.InteropServices.CollectionsMarshal.AsSpan method.
As indicated by the documentation, items should not be added to or removed from the list while using the span or item references taken from the span. Technically this should also be extended to say that the Capacity should not be changed nor should TrimExcess() be called. If these operations are used while actively using the span or its references, then the internal memory of the list may no longer be the same as the memory in the span.
// Create a simple list with three items.
var list = new List<int>();
list.Add(123);
list.Add(456);
list.Add(789);
// Print list to console.
Console.WriteLine("List items:");
foreach (var item in list)
Console.WriteLine(item);
Console.WriteLine();
// Get a reference to the second item in the list.
// WARNING: DO NOT ADD/REMOVE ITEMS FROM THE LIST WHILE USING THIS SPAN
// OR ANY REFERENCES DERIVED FROM THIS SPAN!
var listSpan = CollectionsMarshal.AsSpan(list);
ref var secondItem = ref listSpan[1];
Console.WriteLine($"Referenced value (original): {secondItem}");
// Change the referenced list item.
secondItem = 0;
Console.WriteLine($"Referenced value (modified): {secondItem}");
Console.WriteLine();
// Print the list to console.
Console.WriteLine("List items:");
foreach (var item in list)
Console.WriteLine(item);
Console.WriteLine();
You should get output like this:
List items:
123
456
789
Referenced value (original): 456
Referenced value (modified): 0
List items:
123
0
789
Collections don't work the same way in C# as they do in C++, for example you can add the same object to multiple different collections and so it doesn't really make sense to ask to get a reference to the list that an object is contained in, as it could be in many lists (or none, or even in the same list multiple times)
object myObject = new object();
List<object> list = new List<object>();
list.Add(myObject);
object[] someArray = new object[] { myObject };
Assert.AreEqual(list[0], someArray[0]);
If it helps you can think of lists in C# as being lists of pointers references to the objects being stored where the pointer itself is hidden from you, although understand that in reality the implementation may be more complicated (and is also irrelevant).
If there is a relationship between the objects in a list and the list contents of that list then its up to you to explicitly declare and keep track of what that realtionsip is, for example through a Parent property on the object in the list
List<T> myList = new List<T>();
// Whenever an item is added to myList set the Parent property
myList.Add(item);
item.Parent = myList;
This is what Windows Forms does in order to maintain the relationship between the controls in a container, and the container in which those controls are contained. Obviously you should decide what to do if someone tries to add the same object to multiple lists.
Not unless the type of abc[0] explicitly holds a reference to the list. In fact you cannot do that in C++ either without an explicit reference.
Think about it, in C++ if you can expect firstElemPtr == arrayPtr it is just because arrays store the elements that way and it only works for arrays; everything else is just by accident.
Now consider any list structure that allocates something else (maybe element count) before the pointer to the first element. Your assumption will not work anymore.
If you are designing the type of the items in the collection, then you can add a property to the item's type that "points" to the containing list; when you construct each item, pass in the containing list and save this in the property.
Something like this:
class ListItem
{
public List<ListItem> Parent { get; set; }
public ListItem(List<ListItem> parent)
{
Parent = parent;
}
}
ListItem listItem = new ListItem(abc);
abc.Add(listItem);
// Get collection from item.
List<T> def = listItem.Parent;
Consider:
unsafe static void Main() {
int[] arr = new int[100];
fixed(int* ptr = arr) {
// ptr is a pointer to the zeroth item in the array
}
}
However, unsafe code is not all that common in c# and should be limited to performance-critical regions (and even then used sparingly). In particular, note that we have "pinned" the array by doing this - and note that ptr is only reliable while it is pinned. Outside of the fixed block, GC is free to relocate the array, making ptr invalid.
Clarification: I don't suggest you should do this, but: such things are entirely possible.
List is defined somewhat like this:
public class List<T> : IList<T> blabla
{
private T [] data;
public T this[int index] {
get { return data[index]; }
set { data[index]=value; }
}
... blabla
}
YES, it is NOT linked list of any kind. So you must use it accordingly. Though since .NET value types are small and class types are all references, there usually no heavy array-copy operations as it is possible with C++ for example so such implementation of general-puprose collection is good enough (unless abused). Vector could be a better name from academic standpoint, but it's not.
You would not be able to get a reference to data array, as it's private variable. Nor you would have any need for this. If you need enumerator, use GetEnumerator explicitely or foreach for implicit use.
I'm working with a legacy collection object that only implements non-generic IEnumerable and ICollection. What exactly happens with this object when I try to use this object with a foreach giving a more specific type on the LHS of the foreach expression?
// LegacyFooCollection implements non-generic IEnumerable
LegacyFooCollection collection = GetFooCollection();
foreach (Foo f in collection)
{
// etc.
}
I know (because I've tried it) that this is safe when everything in collection really is of type Foo, but what happens if that fails?
The C# compiler performs the cast implicitly for you. In terms of the casting (but only in those terms1) it's equivalent to:
foreach (object tmp in collection)
{
Foo f = (Foo) tmp;
...
}
Note that this will happen with generic collections too:
List<object> list = new List<object> { "hello", "there", 12345 };
// This will go bang on the last element
foreach (string x in list)
{
}
This is all detailed in section 8.8.4 of the C# 4 spec.
If you're using .NET 3.5 or higher and you want to only select items of the appropriate type, you can use Enumerable.OfType:
LegacyFooCollection collection = GetFooCollection();
foreach (Foo f in collection.OfType<Foo>())
{
// etc.
}
That may not be necessary for a LegacyFooCollection, but it can be useful when you're trying to find (say) all the TextBox controls in a form.
1 The differences are:
In your original code, f is read-only; in the "conversion" it's writable
In your original code, if you capture f you will (currently) capture a single variable across all iterations, as opposed to a separate variable per iteration in the "conversion"
This code will create a runtime type conversion (cast) of each element of the enumerable to Foo. So, if you did foreach (string f in collection) you would get a ClassCastException at runtime, the first time it tries to convert a Foo reference to a String reference.
It will be casting each time. The foreach loop is just using the IEnumerator methods.
Short cast in ForEeach:
foreach (MenuItem menuItem in treeView.ContextMenu.Items.Cast<object>().OfType<MenuItem>()
{
// Do stuff
}