I am having a lot of trouble with Reflection in C# at the moment. The app I am writing allows the user to modify the attributes of certain objects using a config file. I want to be able to save the object model (users project) to XML. The function below is called in the middle of a foreach loop, looping through a list of objects that contain all the other objects in the project within them. The idea is, that it works recursively to translate the object model into XML.
Dont worry about the call to "Unreal" that just modifes the name of the objects slightly if they contain certain words.
private void ReflectToXML(object anObject, XmlElement parentElement)
{
Type aType = anObject.GetType();
XmlElement anXmlElement = m_xml.CreateElement(Unreal(aType.Name));
parentElement.AppendChild(anXmlElement);
PropertyInfo[] pinfos = aType.GetProperties();
//loop through this objects public attributes
foreach (PropertyInfo aInfo in pinfos)
{
//if the attribute is a list
Type propertyType = aInfo.PropertyType;
if ((propertyType.IsGenericType)&&(propertyType.GetGenericTypeDefinition() == typeof(List<>)))
{
List<object> listObjects = (aInfo.GetValue(anObject,null) as List<object>);
foreach (object aListObject in listObjects)
{
ReflectToXML(aListObject, anXmlElement);
}
}
//attribute is not a list
else
anXmlElement.SetAttribute(aInfo.Name, "");
}
}
If an object attributes are just strings then it should be writing them out as string attributes in the XML. If an objects attributes are lists, then it should recursively call "ReflectToXML" passing itself in as a parameter, thereby creating the nested structure I require that nicely reflect the object model in memory.
The problem I have is with the line
List<object> listObjects = (aInfo.GetValue(anObject,null) as List<object>);
The cast doesn't work and it just returns null.
While debugging I changed the line to
object temp = aInfo.GetValue(anObject,null);
slapped a breakpoint on it to see what "GetValue" was returning. It returns a "Generic list of objects" Surely I should be able to cast that? The annoying thing is that temp becomes a generic list of objects but because i declared temp as a singular object, I can't loop through it because it has no Enumerator.
How can I loop through a list of objects when I only have it as a propertyInfo of a class?
I know at this point I will only be saving a list of empty strings out anyway, but thats fine. I would be happy to see the structure save out for now.
Thanks in advance
I'm assuming that the actual value is not a List<object> but is something like a List<string> or List<int> or some other type that isn't exactly object?
If so, then the reason that the cast is failing is because generic classes are neither co- nor contravariant.
In C# 4.0, however, you will be able to make the foreach loop work by casting to IEnumerable<object> because interfaces can be co/contravariant.
(Much) more information here: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx
Edit:
Thinking about it, you don't need generic variance here. List<T> implements the non-generic IEnumerable. This is all you need for the foreach loop to operate, and you only need the elements as type object so just cast it to an IEnumerable instead of List<object> and everything should work fine.
Generics and reflection don't mix well, especially for lists. I would strongly suggest using (non-generic) IList, or if that fails (some generic lists don't implement it), IEnumerable (since IEnumerable<T> : IEnumerable) and invoke the Add manually.
I feel your pain, really (I maintain an OSS serialization API).
In C# 3, you can't cast Lists<T> to other types of Lists<T> even when casting to something like List<Object>. It's explicitly not allowed even when casting to List.
In 4.0 variance changes a little with interfaces with the addition of the in and out keywords.
Here is a link to Eric Lippert explaining how and why this is the case.
http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx
Couldn't you just cast them to objects with OfType()?
List<object> listObjects = anObject.OfType<object>().ToList();
Update:
If the requirement wasn't for a List<object>, this would also work and involve no duplication of the list items:
var enumerableObjects = anObject.OfType<object>();
Related
I have a dynamic type in C# (Content, a type that inherits DynamicObject). It wraps an inner JSON object containing the data. In TryGetMember I return the requested properties from this inner JSON object. This works just fine in case of simple types (e.g. an int or string property), because .Net converts those values correctly.
But I want to use properties with more complex types:
dynamic content = Content.Load(id);
IEnumerable<Book> bookList = content.Books;
The problem is that in TryGetMember of my class I have no way to know the type that I should convert to (in this case the IEnumerable Book), because binder.ReturnType is always Object. According to this article, this is the normal behavior:
Determining the expected type of a DynamicObject member access
But I find this very hard to believe: how is it possible that the API does not let me know the target type? This way I will have to force developers to use the method syntax to specify the type explicitely:
IEnumerable<Books> bookList = content.Books<IEnumerable<Book>>();
...which is ugly and weird.
You could store Type data alongside the JSON serialized data, but that seems like a rather inefficient method of accomplishing what you're trying to do. If your content isn't truely dynamic (e.g., the content changes, but the basic schema of the object is the same), you could just have precompiled classes for each JSON object type, and serialize the JSON into that class once you receive the data. This way, all of the type data would already be recognized by the compiler at runtime.
It turns out that this is not possible indeed. I ended up creating an extension method (defined in two forms: for dynamic collections and JArrays). That way I can at least get a collection of my Content class and all of the following solutions work:
One line, but a bit long
var members = ((IEnumerable<dynamic>)group.Members).ToContentEnumerable();
Separate lines
IEnumerable<dynamic> members = adminGroup.Members;
foreach (dynamic member in members.ToContentEnumerable())
{
//...
}
Using the method call syntax of the extension method (a bit unusual).
var members = ContentExtensions.ToContentEnumerable(group.Members);
I am trying to get the actual object that is contained within a list that itself is contained within a task.
e.g. method has the following signature e.g.
public async Task<List<Dictionary<string,object>>> GetData()
i am currently using something like this:
var member = type.GetMembers()[0];
var returntype = member.ReturnType.GetGenericArguments();
var temp = member.ReturnType.GetGenericArguments()[0];
if (temp.GetGenericArguments().Count() > 0)
{
temp.GetTypeInfo().GetGenericArguments();
var innerttype = temp.GetGenericArguments()[0].FullName;
}
Currently the above code (which is not complete but just an extract from actual code) return system.object as fullname instead of Dictionary.
Any suggestions to solve this are welcome.
If you declare your dictionary to be of type <string, object> and and only ever insert objects inside it and not something higher up in the graph, you'll only ever get object typed objects out of it.
If you're storing all kinds of things in there and need to get their concrete type so you can interact with them, try to make them all conform to an interface first. Then, put the interface type in there replacing "object." If that doesn't work you can use generics. However, you'll still need to know the type ahead of time in order to be able to interact with it.
If you really have no idea what's in there and want to dig into it dynamically, that's precisely what Reflection was built for. You could also look into the dynamic type.
OK. I'm trying to take a List<Product> and pass it into a method that takes a generic List<object> and loops over them to create a List<dynamic> via reflection. This method needs to be able to handle lists of other objects (ie: Item, Customer, Package, etc).
The idea is then so that I'll have a generic list of dynamic that I could send via JSON across the wire.
How can I make this happen?
The simplest approach is to just use LINQ to build a new list. Given that you're using .NET 4, you've got covariance available to you, so you can use:
List<object> foo = products.ToList<object>();
Or even just:
List<object> foo = new List<object>(products);
You can't pass the existing list because a List<Product> simply isn't a List<object>.
Of course if you could change your method to accept an IEnumerable<object> instead, that would work fine due to covariance. That may be feasible if you're only trying to read from the parameter collection.
Mind you, it's not clear why you need to then use reflection to create a List<dynamic> - what's your method actually doing? Why can't you just use:
List<dynamic> foo = new List<dynamic>(products);
I have an hierarchy of classes that I fetch from database by LINQ to SQL controller and the display that big class as needed.Let me explain:
BigProduct
IEnumerable<Review>
//...some generic types
Review
IEnumerable<Comment>
//...some generic types
Comment
//...some generic types
So in my controller I fetch big product and pass it to main view, then pass reviews in foreach to partial views and in each of these partial views I have another foreach that should list all comments as partial view.
The types are defined clear as Ienumerable in classes and this dll worked fine with web forms,now when I hover my mouse over Comment in foreach loop in razor it says it's EntitySet instead of IEnumerable and I get "null exception: acessed context after disposed", that was the reason I am passing it as IEnumerable in first place as I fetch everything in one place. What could be the reason for such behaviour? I didn't change anything in this dll that used to work fine with webforms.
UPDATE: As I mentioned it worked fine with WebForms, I am calling .ToList() to get IEnumerable from IQueryable in when selecting data!
Just because you declare it as IEnumerable<T>, that doesn't change the execution-time type. So when you pass it over to your view, the view would know at compile-time that it's IEnumerable<T> but when you hover over it when debugging, you'll see the actual type. (It's not clear at what point you were hovering over Comment in the razor view, but I strongly suspect it's in the middle of a debugging session.)
If you want to take a copy of the data so that you can dispose the context, just call ToList() and return the result - that will be a List<T> instead.
Note that this compile-time/execution-time difference has nothing to do with LINQ or MVC really. You can see the same thing extremely easily without anything complicated:
object value = "hello";
The compile-time type of value is object, which will stop you calling Substring etc on it. But if you hover over value in a debugging session (or look at it in the Watch or Local window) you'll see that the execution-time type is still string.
EDIT: From your edit:
I am calling .ToList() to get IEnumerable from IQueryable in when selecting data!
I'd really like to see that code, because I don't believe you're simply using something like:
return query.ToList();
If you're returning the result of calling ToList(), that won't be an EntitySet.
When you return a reference to an interface type, like IEnumerable<T>, the reference still points to the object whose run-time type implements the interface. More generally, whenever you refer to an object through an interface that it implements, or through a base class in its inheritance chain, you're still referring to an instance of the object's most-derived type.
For example:
object o1 = new object();
object o2 = "I am a string.";
object o3 = 42;
Console.WriteLine(o1.GetType());
Console.WriteLine(o2.GetType());
Console.WriteLine(o3.GetType());
IComparable ic1 = "I am a string.";
IComparable ic2 = 42;
Console.WriteLine(ic1.GetType());
Console.WriteLine(ic2.GetType());
Output:
System.Object
System.String
System.Int32
System.String
System.Int32
If you want to copy the data to another object that also implements IEnumerable<T>, you need to write some code to do that. As others have suggested, calling ToList() is an easy way to achieve this.
I'm writing a custom deserializer that will deserialize a list by deserializing each of the individual objects in the collection and then putting it together.
Basically my code looks like this:
//myField is a FieldInfo that represents the field we want to put the data in
//resultObject is the object we want the data to go into
List<Object> new_objects = new List<Object>();
foreach (String file_name in file_name_list)
{
Object field_object = MyDeserialization(file_name)
new_objects.Add(field_object)
}
myField.SetValue(resultObject, new_objects);
But this gives an error on the SetValue because (for example) I am trying to put a List(Object) into a List(Int32). Note that this problem only occurs with collections. The following code:
Object new_object = MyDeserialization(file_name)
myField.SetValue(resultObject, new_object)
works just fine provided that the runtime type of the result of MyDeserialization(file_name) is actually compatible with the type of myField. What is the problem here, and is there a way to make the collection deserialization work? (I've tried replacing the List(Object) declaration with myField.FieldType and that won't even compile.
Collections do not offer covariance... a List<int> simply isn't a List<object> (or v.v.). As such, you need to identify the T, for example like so (using the FieldInfo.FieldType) - and create the right type of list in the first place.
For convenience, once created it may be simpler to use the non-generic IList interface:
Type listType = typeof(List<>).MakeGenericType(itemType);
IList list = (IList)Activator.CreateInstance(listType);
list.Add(...); // etc
However; I must stress - writing a full (and robust) serializer is a lot of work. Do you have a specific reason? Many of the inbuilt serializers are pretty good - for example DataContractSerializer - or 3rd party, such as Json.Net, and (if I do say so myself) protobuf-net.
The problem is that .NET can't know that your List is actually a List. The following code should work:
//myField is a FieldInfo that represents the field we want to put the data in
//resultObject is the object we want the data to go into
List<MyType> new_objects = new List<MyType>();
foreach (String file_name in file_name_list)
{
Object field_object = MyDeserialization(file_name)
new_objects.Add((MyType)field_object)
}
myField.SetValue(resultObject, new_objects);
For Fun Linq Extra Credit (assuming file_name_list is IEnumerable):
myField.SetValue(resultObject, file_name_list
.Select(s => MyDeserialization(s))
.Cast<MyType>()
.ToList());