Using reflection I have an object which I need to cast into an iterable list of items (type unknown, will be object). Using the Watch window I can see my object is an array of some type as it tells me the number of elements and I can explode the tree view to see the elements themselves.
Firstly, I need to check that the object passed is some kind of array (might be List, might be object[], etc). Then I need to iterate through that array. However, I can't do the type conversion.
Here's how I'm using it (abbreviated):
private static void Example(object instance, PropertyInfo propInfo)
{
object anArray = propInfo.GetValue(instance, null);
ArrayList myList = anArray as ArrayList;
foreach (object element in myList)
{
// etc
}
}
I've tried various different casts. The above doesn't raise an exception but mylist is null when anArray actually exists and contains items. The actual instance being saved is a strongly-typed List<> but could take a limited subset of forms if necessary. But the point of the exercise is that this Example() method doesn't know the basic type of the property.
Casting it to an ArrayList is only going to work if the object actually is an ArrayList. It wont work with a System.Array, or a System.Collections.Generic.List`1 for example.
I think what you actually should do is cast it to IEnumerable, since that is your only requirement to loop over it...
object anArray = propInfo.GetValue(instance, null);
IEnumerable enumerable = anArray as IEnumerable;
if (enumerable != null)
{
foreach(object element in enumerable)
{
// etc...
}
}
Try to cast to IEnumerable. This is the most basic interface all enumerables, arrays, lists etc. implement.
IEnumerable myList = anArray as IEnumerable;
if (myList != null)
{
foreach (object element in myList)
{
// ... do something
}
}
else
{
// it's not an array, list, ...
}
Simply Try This
string[] arr = ((IEnumerable)yourOjbect).Cast<object>()
.Select(x => x.ToString())
.ToArray();
Try this:
var myList = anArray as IEnumerable;
if (mylist != null)
{
foreach (var element in myList)
{
// etc
}
}
You might also need to specify the generic type of the IEnumerable, depending on your situation.
You should be able to cast it to IEnumerable if it is a collection of any sorts (array, list, etc.). Also PropertyInfo contains a PropertyType property which you could use to find out the actual type if you wanted to.
Just in My case I need to define data type IEnumerable<string>
var myList = anArray as IEnumerable<string>;
Related
The method I am overriding has the following signature.
public override bool IsValid(object value)
The object that is passed in is a List but the the list type is unknown. It could be List<string> or List<int>.
I need to cast this into a List<object>. I've tried
if (!(value is IList temp))
{
return false;
}
List<object> list = temp.OfType<object>().ToList();
which sort of works, but it filters out any null values, presumably because they are not OfType<object>
So what's the best way of doing this?
temp.OfType().ToList();
OfType checks if the element is of the required type, and if so it puts it in a new temporary list.
What you're asking is simply a cast, since every instance in C# is an object: (List<object>)temp. Edit: apparently covariance doesn't work here.
There's absolutely no reason to do this though, you can simply enumerate it as it is: foreach(var obj in (IList)temp). All collections implement the IList non-generic interface.
If the input is either List<int> or List<string> and nothing else, then I think the best approach is to check specifically for these types. So something like:
List<object> list ;
if (value is List<int> listOfInts)
{
list = listOfInts.Cast<object>().ToList();
}
else if (value is List<string> listOfStrings)
{
list = listOfStrings.Cast<object>().ToList();
}
else
{
throw new ArgumentException(...);
}
This might seem redundant, but it might save you a lot of headache down the line, since you will catch if someone passes a List<SomethingElse> or even a List<object> which would be illegal according to your specification.
Using reflection I can obtain the object properties and values for all Data Types and the objects inside this object. But if the object contains a List of other objects I am having trouble in getting the objects in the list. The list can contain any type of object.
private static void SaveObj(object obj) {
foreach (var prop in obj.GetType().GetProperties()) {
if (prop.PropertyType.Namespace == "Entities") { //It is an object
object obL = prop.GetValue(obj, null);
SaveObj(obj);
}
else if (prop.PropertyType.Name == "List`1") { //This is a list of objects
object obP = prop.GetValue(obj);
//obP has the list of objects, I can see the list in debug mode.
List<object> obL = (List<object>)prop.GetValue(obj, null);
//This line returns an exception!!
}
else {
columns += prop.Name.ToLower() + ", ";
values[i] = prop.GetValue(obj, null).ToString();
}
... // the code continues ...
}
}
The exception message returned is:
"It is not possible to convert an object of type 'System.Collections.Generic.List1[Entities.OrderItem]' to type 'System.Collections.Generic.List1[System.Object]'."
Interesting is that I can see all the objects and its contents in degug mode. In Immediate Window I can print the content of the variable obP with all the objects in the list, but how to read them?
Any ideas on how to solve this?
You can try casting it to IEnumerable and then use .Cast<object>().ToList() Like this:
IEnumerable obL = prop.GetValue(obj, null) as IEnumerable;
List<object> list = obL.Cast<object>().ToList();
Sins a List implements the ICollection interface, you could, instead of attempting to cast the returned value to a List<object>, do something like:
ICollection collection = (prop.GetValue(obj, null) as ICollection);
if (collection != null)
{
object[] array = new object[collection.Count];
collection.CopyTo(array, 0);
//if you need a list just create a new one and pass in the array: new List<object>(array);
}
As a side note, wouldn't your:
if (prop.PropertyType.Namespace == "Entities")
{
object obL = prop.GetValue(obj, null);
SaveObj(obj);
}
just cause a infinite loop, which will lead to a StackOverflow/OutOfMemory exception, might want to change it to SaveObj(obL); if that was the intended behavior.
I am using ArrayList in Asp.net I want to extract specific items . My code is
ArrayList items = (ArrayList)Session["mycart"];
foreach(var v in items)
{
}
but this is not working . I want to get value like
v.myvalue;
My arralist is filled with several items coming from prevoius page.
The issue is that ArrayList stores all elements as object. You need to perform a cast to the type of object that contains myvalue.
For example
ArrayList items = (ArrayList)Session["mycart"];
foreach(var v in items)
{
MyObject o = v as MyObject;
if (o != null)
{
// do stuff with o.myvalue
}
}
It may be better to just use the generic List class rather ArrayList, although you may have a perfectly reason for doing otherwise. Generally, you should use the generic (e.g. List<MyObject>), not only for performance but also ease of use.
I'm using Value Injecters to map from 1 type to another using the LoopValueInjection and overriding the SetValue(object v) method with some custom logic. I am trying to detect when a HashSet is being passed in and go through the HashSet and apply a method to each item in it to do some clean up. The issues I'm having are because the parameter is just an object and I don't know what the type of item will be in the HashSet. For example, it could be HashSet or HashSet.
Here is the code I currently have but I'm getting an InvalidCastException.
protected override object SetValue(object v)
{
if (type.Name == "HashSet`1")
{
var genericType = type.GetGenericArguments()[0];
// this line throws the InvalidCastException
var cleanHashSet = (HashSet<object>)Activator.CreateInstance(type);
foreach (var item in (HashSet<object>)v) // I'm sure this cast will throw as well
{
cleanHashSet.Add(Clean(item));
}
return cleanHashSet;
}
return base.SetValue(v);
}
I guess the main question is how can I loop through the HashSet that is passed in as an object once I determine it is in fact a HashSet of some kind? I'm also thinking I will need to create a new empty HashSet of the specific type as well so I can put each item that gets cleaned up into it.
In .NET only interfaces and delegate types can be co- and contra-variant. So it is not possible to cast HashSet<SomeType> to HashSet<object>.
You want to cast your v to non-generic version of IEnumerable interface
dynamic cleanHashSet = Activator.CreateInstance(type);
foreach (object item in (IEnumerable)v)
{
cleanHashSet.Add(Clean(item));
}
If you don't want to use dynamic keyword then you need to call Add method with reflection
object cleanHashSet = Activator.CreateInstance(type);
var method = type.GetMethod("Add");
foreach (object item in (IEnumerable)v)
{
method.Invoke(cleanHashSet, new object[] { Clean(item) });
}
Use:
bool isHashSet = typeof(HashSet<object>).IsAssignableFrom(type);
or
object x = ...
HashSet<object> hs = x as HashSet<object>;
if (hs != null)
{
// use hs
}
If you really have <T> specified then use it instead of object.
HashSet<T> implements ICollection<T>, IEnumerabel<T>, IEnumerable. What means if you know T you can both enumerate and add. Otherwise enumerate only enumerate.
Is there a way to iterate (through foreach preferably) over a collection using reflection? I'm iterating over the properties in an object using reflection, and when the program gets to a type that is a collection, I'd like it to iterate over the contents of the collection and be able to access the objects in the collection.
At the moment I have an attribute set on all of my properties, with an IsCollection flag set to true on the properties that are collections. My code checks for this flag and if it's true, it gets the Type using reflection. Is there a way to invoke GetEnumerator or Items somehow on a collection to be able to iterate over the items?
I had this issue, but instead of using reflection, i ended up just checking if it was IEnumerable. All collections implement that.
if (item is IEnumerable)
{
foreach (object o in (item as IEnumerable))
{
}
} else {
// reflect over item
}
I've tried to use a similar technique as Darren suggested, however just beware that not just collections implement IEnumerable. string for instance is also IEnumerable and will iterate over the characters.
Here's a small function I'm using to determine if an object is a collection (which will be enumerable as well since ICollection is also IEnumerable).
public bool isCollection(object o)
{
return typeof(ICollection).IsAssignableFrom(o.GetType())
|| typeof(ICollection<>).IsAssignableFrom(o.GetType());
}
Just get the value of the property and then cast it into an IEnumerable. Here is some (untested) code to give you an idea:
ClassWithListProperty obj = new ClassWithListProperty();
obj.List.Add(1);
obj.List.Add(2);
obj.List.Add(3);
Type type = obj.GetType();
PropertyInfo listProperty = type.GetProperty("List", BindingFlags.Public);
IEnumerable listObject = (IEnumerable) listProperty.GetValue(obj, null);
foreach (int i in listObject)
Console.Write(i); // should print out 123
Just for information may be it will be of someone's help...
I had a class with nested classes and collection of some other classes. I wanted to save the property values of the class as well nested classes and collection of classes. My code is as follows:
public void LogObject(object obj, int indent)
{
if (obj == null) return;
string indentString = new string(' ', indent);
Type objType = obj.GetType();
PropertyInfo[] properties = objType.GetProperties();
foreach (PropertyInfo property in properties)
{
Type tColl = typeof(ICollection<>);
Type t = property.PropertyType;
string name = property.Name;
object propValue = property.GetValue(obj, null);
//check for nested classes as properties
if (property.PropertyType.Assembly == objType.Assembly)
{
string _result = string.Format("{0}{1}:", indentString, property.Name);
log.Info(_result);
LogObject(propValue, indent + 2);
}
else
{
string _result = string.Format("{0}{1}: {2}", indentString, property.Name, propValue);
log.Info(_result);
}
//check for collection
if (t.IsGenericType && tColl.IsAssignableFrom(t.GetGenericTypeDefinition()) ||
t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == tColl))
{
//var get = property.GetGetMethod();
IEnumerable listObject = (IEnumerable)property.GetValue(obj, null);
if (listObject != null)
{
foreach (object o in listObject)
{
LogObject(o, indent + 2);
}
}
}
}
}
An called this function
LogObject(obj, 0);
However, I have some structs inside my classes and I need to figure out how to get their values. Moreoevr, I have some LIst. I need to get their value as well.... I will post if I update my code.
The best you could probably do would be to check if the object implements certain collection interfaces - probably IEnumerable would be all that you need. Then it's just a matter of calling GetEnumerator() off of the object, and using IEnumerator.MoveNext() and IEnumerator.Current to work your way through the collection.
This won't help you if the collection doesn't implement those interfaces, but if that's the case it's not really much of a collection, I suppose.
When your using reflection you aren't necessarily using an instance of that object. You would have to create an instance of that type of be able to iterate through the object's properties. So if you are using reflection use the ConstructorInfo.Invoke() (?) method to create a new instance or point to an instance of the type.
I would look at the Type.FindInterfaces method. This can filter out the interfaces implemented by a given type. As in PropertyInfo.PropertyType.FindInterfaces(filterMethod, filterObjects). You can filter by IEnumerable and see if any results are returned. MSDN has a great example in the method documentation.
If you're not using an instance of the object but rather a Type, you can use the following:
// type is IEnumerable
if (type.GetInterface("IEnumerable") != null)
{
}
A rather straightforward approach would be to type cast the object as the collection and directly use that.