How to use SetValue of an Indexed Property in Reflection? - c#

I have a C# Converter method which convers generic lists with the use of reflection.
The problem occurs when I try to call the SetValue method of the Item's property, it throws the following inner exception (ArgumentOutOfRangeException):
Index was out of range. Must be
non-negative and less than the size of the collection. Parameter name:
index.
Here is my code:
internal class Program
{
private static void Main()
{
List<ClassA> classA = new List<ClassA>();
classA.Add(new ClassA { Data = "value1" });
classA.Add(new ClassA { Data = "value2" });
List<ClassB> classB = Converter<List<ClassA>, List<ClassB>>(classA);
}
public static TOut Converter<TIn, TOut>(TIn request)
{
var response = Activator.CreateInstance<TOut>();
PropertyInfo propertyA = typeof(TIn).GetProperty("Item");
PropertyInfo propertyB = typeof(TOut).GetProperty("Item");
int count = (int)typeof(TIn).GetProperty("Count").GetValue(request);
for (int i = 0; i < count; i++)
{
var value = propertyA.GetValue(request, new object[] { i });
var b = CreateBFromA(propertyB, propertyA, value);
propertyB.SetValue(response, b, new object[] { i });
}
return response;
}
private static object CreateBFromA(PropertyInfo propertyB, PropertyInfo propertyA, object value)
{
var b = Activator.CreateInstance(propertyB.PropertyType);
object o = propertyA.PropertyType.GetProperty("Data").GetValue(value);
propertyB.PropertyType.GetProperty("Data").SetValue(b, o);
return b;
}
}
internal class ClassA
{
public string Data { get; set; }
}
internal class ClassB
{
public string Data { get; set; }
public object Other { get; set; }
}
This is a small example code of a bigger generic method (where I need to use reflection), so you can try and run it to regenerate the exception.
How to use the SetValue method to avoid this exception?

Here is my aproach to it:
public static TCollectionOut ConvertCollection<TCollectionIn, TCollectionOut, TIn, TOut>(TCollectionIn input)
where TCollectionIn : IEnumerable<TIn>
where TCollectionOut : ICollection<TOut>, new()
where TOut : new()
{
var res = new TCollectionOut();
foreach (dynamic item in input)
{
dynamic o = new TOut();
ConvertItem(item, o);
res.Add(o);
}
return res;
}
public static TCollectionOut ConvertCollectionMoreDynamic<TCollectionIn, TCollectionOut>(TCollectionIn input)
where TCollectionIn : IEnumerable
{
dynamic res = Activator.CreateInstance(typeof (TCollectionOut));
var oType = typeof (TCollectionOut).GetMethod("Add").GetParameters().Last().ParameterType;
foreach (dynamic item in input)
{
dynamic o = Activator.CreateInstance(oType);
ConvertItem(item, o);
res.Add(o);
}
return res;
}
public static void ConvertItem(ClassA input, ClassB output)
{
output.Data = input.Data;
}
If you wich to support more types just create ConvertItem method with correct overload.

This is because you are trying to pass an index to a not indexed property (Data).
If you post the ClassA code I can try yo help. Anyway you can use LINQ to perform this kind of conversions. It's faster (to write and to execute) and type safe.

Related

Interface To Generic Casting issue

I'm implementing a custom data store against an in memory state tree and I'm running into some issues with my indexing. My indexes are meant to be covering, so they should return the object not just a position. An index has a name, and a List of objects. Those objects can be different underlying types so the indexed objects are IHasUUID which indicates an item has a UUID.
public class DataIndex
{
public string Name;
public IDictionary<string, List<IHasUUID>> Index;
}
public class Indexer
{
private List<DataIndex> Indexes;
...
public List<IHasUUID> GetIndexedItems(List<IHasUUID> indexBy)
{
var indexer = GetIndexByKeys<IHasUUID>(indexBy);
var indexHash = GetHashKey(indexBy);
return GetIndexValues<IHasUUID>(indexer, indexHash);
}
private List<T> GetIndexValues<T>(DataIndex indexBy, string indexHash) where T : IHasUUID
{
if (indexBy == null)
return new List<T>();
return ((IList<T>)indexBy.Index[indexHash]).ToList();
}
}
I generate the key to the dictionary using a reflection method where I look at the things being used as the index key and append the type string names
So I ask my Engine to FindRecords, no problem
public List<T> FindRecords<T>(IHasUUID indexBy) where T : IHasUUID
{
var indexedIds = Indexer.GetIndexedItems(new List<IHasUUID>() { indexBy });
return ((IList<T>)indexedIds).ToList();
}
Here I run into a wall on the FindRecords return
I have
return ((IList<T>)indexedIds).ToList();
and I tried
return indexedIds.ToList();
Neither one is able to cast up to T. Is this possible?
Thanks in advance
EDIT
I do seem to be much closer,
public class DataIndex
{
public DataIndex()
{
Index = new Dictionary<string, IEnumerable<IHasUUID>>();
}
public string Name;
public Dictionary<string, IEnumerable<IHasUUID>> Index;
}
public class Indexer
{
private List<DataIndex> Indexes;
public Indexer()
{
Indexes = new List<DataIndex>();
}
public IEnumerable<T> GetIndexedItems<T>(IEnumerable<IHasUUID> indexBy) where T : IHasUUID
{
var indexer = GetIndexByKeys<T>(indexBy);
var indexHash = GetHashKey(indexBy);
return GetIndexValues<T>(indexer, indexHash);
}
private IEnumerable<T> GetIndexValues<T>(DataIndex dataIndex, string indexHash) where T : IHasUUID
{
if (dataIndex == null)
return new List<T>();
return dataIndex.Index[indexHash].ToList() as List<T>;
}
}
However I am getting null back from GetIndexValues. I also tried returning it as an IEnumerable, also null
Here's my Add to index method
public void AddManyToIndex<T>(IEnumerable<IHasUUID> keys, IEnumerable<IHasUUID> newItems) where T : IHasUUID
{
var index = GetIndexByKeys<T>(keys) ?? CreateIndex<T>(keys);
string indexKey = GetHashKey(keys);
if (!index.Index.ContainsKey(indexKey))
{
index.Index[indexKey] = new List<IHasUUID>();
}
var list = index.Index[indexKey].ToList();
list.AddRange(newItems.ToList());
index.Index[indexKey] = list as IEnumerable<IHasUUID>;
}
System.Collections.Generic.List<T> is not covariant. That is to say that, given two types T and U where a U is a T, a List<U> is not a List<T>.
This is why the cast fails, a list of a type implementing IHasUUID, T in your example, is not a List<IHasUUID>.
There are however, covariant1 generic types, such as System.Collections.Generic.IEnumerable<T> and System.Collections.Generic.IReadOnlyList<T>. For such types, given two types T and U where a U is a T, an IEnumerable<U> is an IEnumerable<T>.
In addition to solving your specific problem, using such types will also serve to make your APIs more flexible while at the same time making your implementation simpler and easier.
Consider the following:
public interface IHasUuid
{
Guid Uuid { get; }
}
public class DataIndex
{
public string Name { get; set; }
public IDictionary<string, IEnumerable<IHasUuid>> Index { get; } = new Dictionary<string, IEnumerable<IHasUuid>>();
}
public class Indexer
{
public IEnumerable<IHasUuid> GetIndexedItems(IEnumerable<IHasUuid> indexBy)
{
var indexer = GetIndexByKeys<IHasUuid>(indexBy);
var indexHash = GetHashKey(indexBy);
return GetIndexValues<IHasUuid>(indexer, indexHash);
}
private IEnumerable<T> GetIndexValues<T>(DataIndex dataIndex, string hash) where T : IHasUuid
{
if (dataIndex == null)
return Enumerable.Empty<T>();
return dataIndex.Index[hash] as IEnumerable<T>;
}
}
You can store any type that implements IEnumerable<IHasUuid> in DataIndex.Index. All generic collections in .NET implement this interface, including List<T>, HashSet<T>, ConcurrentQueue<T> and countless more.
If you wish to retain the defensive copying in the orginal code, which may well be wise, simply add the .ToWhatever() back to the code.
private IEnumerable<T> GetIndexValues<T>(DataIndex dataIndex, string hash) where T : IHasUuid
{
if (dataIndex == null)
return Enumerable.Empty<T>();
return (dataIndex.Index[hash] as IEnumerable<T>).ToHashSet();
}
For example, you can build up a DataIndex instance like this
class Person: IHasUuid {
public Guid Uuid { get; }
public string Name { get; }
}
var index = new DataIndex {
Index = {
["People"] = new List<Person>()
}
};
var indexer = new Indexer();
var people = indexer.GetIndexValues(index, "People");
Here's a working fiddle: https://dotnetfiddle.net/qgjXR7
1: A type is covariant over its type parameter if that type parameter is declared using the out modifier. As its name suggests, the out modifier means that type parameter to which it is ascribed may only be used in output positions in the declaring type.
interface Wrapper<out T>
{
T Value { get; } // OK
T Value { get; set; } // Error
void SetValue(T value); // Error
}
Interface and delegate types can declare covariant type parameters, concrete types such as classes and structs may not.

Build dynamic predicate based on generic type

How do I make this expression dynamic based on the generic type passed in the parameter?
In the simplified form:
public static class CompareService
{
public static List<T> Run<T>(List<T> database_list, string directory_path)
{
var csv_list = CompareService.MergeRecordsFromFiles<T>(directory);
return CompareService.RunComparison<T>(database_list, csv_list);
}
public static T CompareData<T>(List<T> database_list, List<T> csv_list)
{
var diff = new List<T>();
foreach (var db_item in database_list)
{
// ...
// if T is of type Deathstar compare reference_number property
// if T is of type Stormtrooper compare id property
// if T is of type Sith compare id and anger_level property
var csv_item = csv_list.FirstOrDefault(x => x.reference_number == db_item.reference_number);
// Comparison code
ComparisonResult result = compareLogic.Compare(db_item, csv_item);
// ...
}
return diff;
}
}
It is called from another generic service:
public static void Whatever<T>(List<T> list)
{
// ...
var directory_path = "C:\";
var delta = CompareService.CompareData<T>(list, directory_path);
// ...
}
The most naive implementation would be to check if your itemToFind can be cast to DeathStar, StormTrooper or Sith and if so call the instances property.
var deathStar = itemToFind as DeathStar;
if(deathStar != null)
return database_list.Where(x => ((DeathStar)x).reference_number == deathStar.reference_number).FirstOrDefault();
else
{
var sith = itemToFind as Sith;
if(sith != null)
return database_list.Where(x => ((Sith)x).anger_level == sith.anger_level).FirstOrDefault();
else
return database_list.Where(x => ((StormTrooper)x).id== ((StormTrooper)item).id).FirstOrDefault();
}
This is quite cumbersome, including many casts. In particular it completely bypasses the actual benefits of generics using any arbitrary type (that fullfills the constraints if existing). In your case you´d have a generic method that will only wortk for three decent types.
A better approach is to let all your classes implement a common interface that defines a property, for instance:
interface IObject {
int Level { get; }
}
Now all classes define that level-property:
clas DeathStar : IObject
{
public int Level { get { return this.reference_number; } }
}
clas Sith : IObject
{
public int Level { get { return this.anger_level; } }
}
clas StormTrooper: IObject
{
public int Level { get { return this.id; } }
}
Than you can use a constraint on your type T to implement that interface:
public static T CompareData<T>(List<T> list, T itemToFind) where T: IObject
Why not like this:
public static T CompareData<T>(List<T> list, Func<T, bool> predicate)
{
return database_list.FirstOrDefault(predicate);
}
And then use it like this:
var itemToFind = new ItemToFind();
var myObjectList = new List<MyObject>();
var item = CompareData<MyObject>(myObjectList, x=> x.MyObjectProperty == itemToFind.Id);
You could add a property selector:
public static class CompareService
{
public static T CompareData<T>(this List<T> list, T itemToFind, Func<T, int> propSelector)
{
int propToFind = propSelector(itemToFind); // cache
return database_list.FirstOrDefault(x => propSelector(x) == propToFind);
}
}
And call it like that:
listOfDeathstars.CompareData(deathStarToFind, ds => ds.reference_number);
listOfStormtroopers.CompareData(trooperToFind, t => t.id);
listOfSiths.CompareData(sithStarToFind, sith => new { sith.id, sith.anger_level});
Note: I added the this keyword in the signature to make it an extension (not sure if you intended that but forgot the keyword). And Where(predicate).FirstOrDefault() can be reduced to FirstOrDefault(predicate).

Method returning value depending of collection item type

The code below add some object in MemoryCache. These objects can have different type.
I'd like a method able to return the object from MemoryCache but the return type can be different.
In my sample it's 2 but can be much more. In my sample, the type return are IT1 or List<IT2>
How can I implement this method ?
I'd like method like this (the type returned can be different depending the key) :
public ??? GetObjectFromKey(string key)
{
return _cache.Get(key);
}
Thanks,
MemoryCache _cache = MemoryCache.Default;
var it1 = new T1 { Name = "My" };
var it2 = new List<IT2>().Add(new T2 { Age = 5 });
_cache.Add("ITC1", it1, new CacheItemPolicy());
_cache.Add("ITC2", it2, new CacheItemPolicy());
var typeName = _cache.Get("ITC1").GetType();
public interface IT1
{
string Name { get; set; }
}
public class T1 : IT1
{
public string Name { get; set; }
}
public class T2 : IT2
{
public int Age { get; set; }
}
public interface IT2
{
int Age { get; set; }
}
The return type of your cache has to be either object or dynamic. You have no other possibility, because the classes you put into your cache have nothing in common.
Generics?
public T GetObjectFromKey<T>(string key)
{
return (T)_cache.Get(key);
}
If you know the type when you are calling the GetObjectFromKey you can use generics:
public T GetObjectFromKey(string key)
{
object returnObj = _cache.Get(key);
if(returnObj.GetType() == typeof(T)) // may need to also check for inheritance
{
return (T) returnObj;
}
else
{
throw new Expcetion("InvalidType");
}
}
Then when you call it:
IT1 myObj = GetObjectFromKey<IT1>("mykey");
As promised, here is how you can construct the generic method from an arbitrary type at run time (though I don't see how this is going to help!):
Type t = typeof(Something); // your type at run time
Type cacheType = _cache.GetType(); // The type that has the GetObjectFromKeyMethod
MethodInfo lGenericMethod = cacheType.GetMethod("GetObjectFromKey");
MethodInfo lTypedMethod = lMethod.MakeGenericMethod(t);
dynamic lReturn = lTypedMethod.Invoke(_cache, new object[] { "mykey" } );
Though clearly you can't do anything with lReturn as you don't know the type at compile time and you could have just returned an object (or else some common interface) and called GetType on that. Still, it is fun to write fun reflection methods like that :P

PropertyInfo.SetValue() not working but no errors

I'm writing my own method to convert an object graph to a custom object since the JavaScriptSerializer fires errors on null values.
So this is what I have so far:
internal static T ParseObjectGraph<T>(Dictionary<string, object> oGraph)
{
T generic = (T)Activator.CreateInstance<T>();
Type resType = typeof(T);
foreach (PropertyInfo pi in resType.GetProperties())
{
object outObj = new object();
if (oGraph.TryGetValue(pi.Name.ToLower(), out outObj))
{
Type outType = outObj.GetType();
if (outType == pi.PropertyType)
{
pi.SetValue(generic, outObj, null);
}
}
}
return generic;
}
Now the pi.SetValue() method runs, and doesn't fire an error but when I look at the properties of generic, it's still the same as it was before hand.
The first property it goes through is a boolean so the values end up like this
generic = an object of type MyCustomType
generic.property = false
outObj = true
pi = boolean property
outType = boolean
Then after the SetValue method runs, generic.property is still set to false.
PropertyInfo.SetValue/GetValue worked with struct with accurate using
struct Z
{
public int X { get; set; }
}
Z z1 = new Z();
z1.GetType().GetProperty("X").SetValue(z1, 100, null);
Console.WriteLine(z1.X); //z1.X dont changed
object z2 = new Z();
z2.GetType().GetProperty("X").SetValue(z2, 100, null);
Console.WriteLine(((Z)z2).X); //z2.x changed to 100
Z z3 = new Z();
object _z3 = z3;
_z3.GetType().GetProperty("X").SetValue(_z3, 100, null);
z3 = (Z)_z3;
Console.WriteLine(z3.X); //z3.x changed to 100
Correct way to change struct:
box struct
change property of boxed struct
assign boxed struct to source
Found the answer. Apparently, PropertyInfo.SetValue() and PropertyInfo.GetValue() do not work for structures, only classes.
MyCustomType was unfortunately a struct, so changing this to a class made it work.
The 3rd reply in this thread states why structures do not work and classes do.
EDIT: It does work with structs, see the marked answer.
So I took your method and made a unit test of it:
class PropertySetTest
{
static readonly Type resType = typeof(Car);
internal static T ParseObjectGraph<T>(Dictionary<string, object> oGraph)
{
T generic = (T)Activator.CreateInstance<T>();
foreach (PropertyInfo pi in resType.GetProperties())
{
//No need to new() this
object outObj; // = new object();
if (oGraph.TryGetValue(pi.Name.ToLower(), out outObj))
{
Type outType = outObj.GetType();
if (outType == pi.PropertyType)
{
pi.SetValue(generic, outObj, null);
}
}
}
return generic;
}
[Test]
public void Test()
{
var typeData = new Dictionary<String, Object> {{"color", "Blue"}};
var myCar = ParseObjectGraph<Car>(typeData);
Assert.AreEqual("Blue", myCar.Color);
}
}
internal class Car
{
public String Color { get; set; }
}
This passes. Can you make it not pass in the way that you are seeing?
EDIT: With your struct, it is only marginally more complicated. See Jon Skeet's answer here regarding what's going on. As for the working code:
class PropertySetTest
{
static readonly Type resType = typeof(Car);
internal static T ParseObjectGraph<T>(Dictionary<string, object> oGraph)
{
Object generic = Activator.CreateInstance<T>();
foreach (var pi in resType.GetProperties())
{
//No need to new() this
object outObj; // = new object();
if (oGraph.TryGetValue(pi.Name.ToLower(), out outObj))
{
var outType = outObj.GetType();
if (outType == pi.PropertyType)
pi.SetValue(generic, outObj, null);
}
}
return (T)generic;
}
[Test]
public void Test()
{
var typeData = new Dictionary<String, Object> {{"color", "Blue"}};
var myCar = ParseObjectGraph<Car>(typeData);
Assert.AreEqual("Blue", myCar.Color);
}
}
internal struct Car
{
public String Color { get; set; }
}

Casting to a collection with a dynamic type

How can I set generic type dynamically?
public class A
{
public int X { get; set; }
public A()
{
X = 9000;
}
}
public class Class1
{
public void Test()
{
List<A> theList = new List<A>() {
new A { X = 1 },
new A { X = 2 }
};
object testObj = theList;
var argType = testObj.GetType().GetGenericArguments()[0];
Foo(testObj as ICollection<argType>); // ?
}
public void Foo<T>(ICollection<T> items) where T:new()
{
T newItem = new T();
items.Add(newItem);
}
To do in "regular" c# you would use reflection to obtain the MethodInfo, then use MakeGenericMethod() and Invoke(). However, this is easier:
Foo((dynamic)testObj);
The reflection approach here is:
var method = typeof(Class1).GetMethod("Foo").MakeGenericMethod(argType);
method.Invoke(this, new object[] { testObj });
You can't do that, because in the Foo function you are supposed to do something with the collection, and there's no guarantee that the type will be safe.
The only way is using an "object" then casting to the proper type within the Fooo function.

Categories