Accessing items in an IList with C# reflection - c#

I have a class with a Items property, which is an IList:
class Stuff {
IList<OtherStuff> Items;
}
I want to be able to receive a string within a method (I thought of this format originally: Items[0]) and be able to retrieve the first item of the Items list.
I tried this:
object MyMethod(string s, object obj) {
return obj.GetType().GetProperty(s).GetValue(obj,null);
}
with s being 'Items[0]' but it doesn't work... Also tried parsing the parameter to access only the property 'Items' of the object and then accessing the index (knowing that it is an IList).
None of these approaches worked... Any thoughts?
Any thoughts?

You can access the property and then can you convert it to a list.
T GetListItem<T>(object obj, string property, int index)
{
return (obj.GetType().GetProperty(property).GetValue(obj, null) as IList<T>)[index];
}
Working example for your sample code:
OtherStuff item = GetListItem<OtherStuff>(obj, "Items", 0);

If you want to test an object to see if it has a numeric indexer, without regard to whether it is an IList, and then invoke the indexer via reflection, you can try out this method.
It returns true if the object has an indexer, and populates value with the value of the 0th index as well.
public static bool TryGetFirstIndexWithReflection(object o, out object value)
{
value = null;
// find an indexer taking only an integer...
var property = o.GetType().GetProperty("Item", new Type[] { typeof(int) });
// if the property exists, retrieve the value...
if (property != null)
{
value = property.GetValue(list, new object[] { 0 });
return true;
}
return false;
}
Note that this example makes no attempt to gracefully handle exceptions, such as IndexOutOfRangeException. That's up to you to add if you find it relevant.

Items was not a property, so my approaches wouldn't work. It should be, so I transformed it into a property and now it is working smoothly.

You should try this:
object GetFirstItemByReflection(object obj) {
return obj.GetType().GetMethod("get_Item").Invoke(obj, new object[] { 0 } );
}
with the appropriate checks.
"get_Item" is the "generated" method used when you access items by index in a collection.
When you get its MethodInfo, you invoke it on your collection, passing it the "0" parameter, to get the first item.

Related

How to access methods of subclass in object array C#?

How can I set/get the value of an object in an object array?
Currently I get:
"object does not contain a definition for 'value' and no extension method"
Example C#;
public class myObjClass
{
public int value = 5;
}
public class myObjClass2
{
public float[] pos = new float[2];
}
public void test()
{
myObjClass myObj = new myObjClass();
myObjClass2 myObj2 = new myObjClass2();
object[] objArr = new object[2];
objArr[0] = myObj;
objArr[1] = myObj2;
Debug.Print(myObj.value.ToString());
Debug.Print(objArr[0].value.ToString()); // how?
}
Its because a generic object does not have the property value your class myObjClass has. To fix this you could cast the item to your class like so:
((myObjClass)objArr[0]).value.ToString()
Only do this ^ if you are sure of the type
Instead you could also check it first:
With as:
var item = objArr[0] as myObjClass;
if( item != null ) // Item will be null if its not a 'myObjClass'
{
//Do stuff with item
}
Or with is:
if( objArr[0] is myObjClass )
{
var item = (myObjClass)objArr[0];
//Do stuff with item
}
When using an object array you have to cast to the real type (here: myObjClass) before accessing the fields:
You can access the object like this
((myObjClass)objArr[0]).value
but I would not recommend. CanĀ“t you have your array to be the concrete type
var array = new myObjClass[42]
A compact safe alternative to retrieve the value is
(objArr[0] as myObjClass)?.value
You need to cast object to known type which is myObjClass, like:
((myObjClass)objArr[0]).value.ToString();
Or you can use reflection
var valueString = objArr[0].GetType().GetProperty("value").GetValue(objArr[0]);
Debug.Print(valueString.ToString());
Hope helps,
Technically you can put it as
Debug.Print((objArr[0] as myObjClass)?.value.ToString());
We try casting objArr[0] as myObjClass and if succeed get value and turn it to string. If objArr[0] is not myObjClass we return null as a string
However, a much better way is to implement ToString() in both classes of interest:
public class myObjClass
{
public int value = 5;
public override string ToString() {
// When debugging we want to know "value"
return value.ToString();
}
}
public class myObjClass2
{
public float[] pos = new float[2];
public override string ToString() {
// When debugging we want to know "pos[0]" and "pos[1]" values
return $"{pos[0]} : {pos[1]}";
}
}
And then put an easy
// Just print out debug info (we don't want to know the actual objArr[0] class)
Debug.Print(objArr[0].ToString());
You have a single object, that indeed is an instance of myObjClass, and has a value field, but you have two references to it.
One (myObj) is known to the compiler to be of type myObjClass, and it can guarantee that it has a value field.
The other (objArr[0]) is only known to the compiler to be of type object, and it cannot guarantee that it has a value field.
For example, you could do:
objArr[0] = (random.Next() > 0.5) : myObj ? myObj2
where we're gonna decide at runtime, based on the value of a random number, which will be the type of the actual object at objArr[0].
So, if this was allowed, half of the time objArr[0].value would be correct, and half of the time it will be an error.

Universal Add method

I want to create a method that is able to add an item to a collection whose type is unknown at compile time. It will receive two objects: the collection and the item.
Currently, I have this:
public void Add(object collection, object item)
{
var list = (IList)collection;
list.Add(item);
}
The problem? It doesn't work when the collection is a, for example, an instance of this type:
public sealed class ColumnDefinitionCollection : IList<ColumnDefinition>, IEnumerable<ColumnDefinition>
{
}
What can I do to make it work with every kind of instance that has an Add method?
EDIT: I'm tried with the method proposed by #lukegv, but I'm getting this when the collection is a ColumnDefinitionCollection (running in a Universal Windows Application):
I created another question related to this issue ("Add" not being retrieved) Unable to get the "Add" method of a ColumnDefinitionCollection in UWP
I tried to extend Zdravko Danevs answer by some checks to prevent undesired behavior like the one you and Jon Skeet mentioned (DateTime.Add).
This one checks whether the type expected by the collections Add-method equals the type provided by the item.
static void AddToCollection(object collection, object item)
{
MethodInfo addMethod = collection.GetType().GetMethod("Add");
if (addMethod == null || addMethod.GetParameters().Length != 1)
{
// handle your error
return;
}
ParameterInfo parameter = addMethod.GetParameters().First();
if (parameter.ParameterType.Equals(item.GetType()))
{
addMethod.Invoke(collection, new object[] { item });
}
else
{
// handle your error
}
}
While this is probably not what you want to do, you can use reflection to check if there is an Add method and call it with the parameter supplied. Something like this (untested):
Type t = collection.GetType();
MethodInfo add = t.GetMethod("Add");
if (add != null)
{
params = new object[] { item };
add.Invoke(collection, params);
}

Getting specific properties from an object child

Struggled to come up with a decent way to ask/title this question, but will try and illustrate it as best I can.
I am working with a data structure something like this:
public Foo
{
public Bar Bar {get;set;}
}
public Bar
{
public SubTypeA TypeA {get;set;}
public SubTypeB TypeB {get;set;}
...
}
public SubTypeA
{
public int Status {get;set;}
...
}
Note that I am unable to change the data structure for this.
There are many different types in the Bar class, which all have different properties within them, but common to all of them is the property of Status.
What I need to do, is given an object of type Foo, is record the statuses for every item in the Bar object within it. Not every SubType is going to have a value every time though, some could be null.
I can sort of manage it by using a recursive function like below to loop through all the properties. It isn't ideal though I don't think as the loop could get quite large as there could be a lot of properties on each SubType.
private void GetProperties(Type classType, object instance)
{
foreach (PropertyInfo property in classType.GetProperties())
{
object value = property.GetValue(instance, null);
if (value != null)
{
if (property.Name == "Status")
{
Record(classType, value);
}
GetProperties(property.PropertyType, value);
}
}
}
Is this about the only approach that there is for such a problem?
EDIT: Going by the answer given by Selman22, I have come up with another issue wherein I am trying to create an anonymous object based on the status and name of object.
var z = instance.GetType()
.GetProperties()
.Select(x => new
{
status = x.GetValue(instance).GetType().GetProperty("status").GetValue(x, null),
name = x.Name
})
.ToList();
This is throwing an error of Object does not match target type. when trying to retrieve the value. Is this possible in a 1 liner?
Type class contains GetProperty(string name, BindingFlags method) that you can use to retrieve specific property. Instead of looping through every property use this method.
http://msdn.microsoft.com/en-us/library/system.type.getproperty(v=vs.110).aspx
// Get Type object of MyClass.
Type myType=typeof(MyClass);
// Get the PropertyInfo by passing the property name and specifying the BindingFlags.
PropertyInfo myPropInfo = myType.GetProperty("MyProperty", BindingFlags.Public | BindingFlags.Instance);
You can get all Status properties using LINQ instead of recursion:
var barInstance = typeof(Foo).GetProperty("Bar").GetValue(fooInstance);
var statusProperties = barInstance.GetType()
.GetProperties()
.Select(x => x.GetValue(barInstance).GetType().GetProperty("Status"));

How can I migrate from dictionary["key"] to ObjectDictionary.key?

It seems like ViewBag.SomeKey works a lot like a php array in the sense that it seems to offer no compiletime checking of key names. I am wondering if there is a one-to-one correspondence with ViewBag and some dictionary class with extra methods, ie if ViewBag.SomeKey works in the same manner as myDictionary["SomeKey"].
Also, I am wondering how I might convert a dictionary into a dynamic object.
ViewBag is a dynamic wrapper around ViewData, which is a dictionary (ViewDataDictionary). Writing ViewBag.SomeKey is the same as ViewData["SomeKey"]
You can initialize it like this :
foreach(var item in myDictionary)
{
ViewData[item.Key] = item.Value;
}
Each item will be available as ViewData["Key"] or ViewBag.Key.
example from msdn
// The class derived from DynamicObject.
public class DynamicDictionary : DynamicObject
{
// The inner dictionary.
Dictionary<string, object> dictionary = new Dictionary<string, object>();
public DynamicDictionary(Dictionary<string, object> d) { dictionary = d; }
public DynamicDictionary() { }
// This property returns the number of elements
// in the inner dictionary.
public int Count
{
get
{
return dictionary.Count;
}
}
// If you try to get a value of a property
// not defined in the class, this method is called.
public override bool TryGetMember(
GetMemberBinder binder, out object result)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
string name = binder.Name.ToLower();
// If the property name is found in a dictionary,
// set the result parameter to the property value and return true.
// Otherwise, return false.
return dictionary.TryGetValue(name, out result);
}
// If you try to set a value of a property that is
// not defined in the class, this method is called.
public override bool TrySetMember( SetMemberBinder binder, object value)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
dictionary[binder.Name.ToLower()] = value;
// You can always add a value to a dictionary,
// so this method always returns true.
return true;
}
}
class Program
{
static void Main(string[] args)
{
// Creating a dynamic dictionary.
dynamic person = new DynamicDictionary(/*this can be your dictionary*/);
// Adding new dynamic properties.
// The TrySetMember method is called.
person.FirstName = "Ellen";
person.LastName = "Adams";
// Getting values of the dynamic properties.
// The TryGetMember method is called.
// Note that property names are case-insensitive.
Console.WriteLine(person.firstname + " " + person.lastname);
// Getting the value of the Count property.
// The TryGetMember is not called,
// because the property is defined in the class.
Console.WriteLine( "Number of dynamic properties:" + person.Count);
// The following statement throws an exception at run time.
// There is no "address" property,
// so the TryGetMember method returns false and this causes a
// RuntimeBinderException.
// Console.WriteLine(person.address);
}
}
// This example has the following output:
// Ellen Adams
// Number of dynamic properties: 2

How to access the Index Of A Generic.List By Reflection?

ok, ive a class and i pass an object as property.
the object that i pass is a List<X>
in my class im trying to access the Object index by reflection BUT I CAN'T!!!
Example:
this class works i just wrote down the part i want to show you and i need help.
class MyClass
{
private object _recordSet;
public object RecordSet
{
get { return _recordSet; }
set { _recordSet = value; }
}
public string Draw()
{
system.reflection.Assembly asem = system.reflection.Assembly.getAssembly(_dataSource.GetType());
object instance;
instance = asem.CreateInstance(_dataSource.GetType().UnderlyingSystemType.FullName);
//to access de Count of my List
int recordcount = int.Parse(_dataSource.GetType().GetProperty("Count").GetValue(_dataSource,null));
//i need to do a
for(int cont = 0; cont < recordCount; cont++)
{
_dataSource[cont].Name; // <-- THIS PART IS NOT WORKING!!! because i cant access the Index Directly.... WHAT TO DO!! ???
}
}
}
If you are using reflection (and hence lots of object), why not just cast as an IList (non-generic) instead?
i.e.
IList list = (IList)actualList;
object foo = list[17];
Also - for your original code with Count, you don't mean int.Parse - you should just cast (since we expect Count to be an int).
Just cast your object to a list first, you don't need reflection here.

Categories

Resources