Reputation: 223
I have a Dictionary:
Dictionary<int, Type> AllDrillTypes = new Dictionary<int, Type>()
{
{13,typeof(TCHEMISTRY)},
{14,typeof(TDRILLSPAN)}
};
where TCHEMISTRY and TDRILLSPAN are classes. Then I want to get rows from one of this classes like this:
Type T = AllDrillTypes[13];
var LC = Activator.CreateInstance( typeof(List<>).MakeGenericType( T ) );
MethodInfo M = T.GetMethod("FindAll", BindingFlags.Public | BindingFlags.Static, null, new Type[] { }, null);
LC = M.Invoke(null, new object[] { });
All this code works correctly. After that I need to get some rows like this:
var LingLC = from obj in LC where obj.RunID == 1001 select obj;
But this line causes error:
"Could not find an implementation of the query pattern for source type 'object'. 'Where' not found."
What's wrong with this code line?
Upvotes: 5
Views: 1197
Reputation: 560
try
IEnumerable returnedObjects = (IEnumerable)M.Invoke(null, new object[] { }) as IEnumerable;
then iterate through your ienumerable
foreach (object report in returnedObjects)
{
// Use reflection to read properties or add to a new List<object> if you
// need an ICollection<object>
}
instead of :
LC = M.Invoke(null, new object[] { });
Upvotes: 0
Reputation: 20054
Maybe you could rewrite your code to something like this, .... to get a more type-safe solution (without using reflection).
void Main()
{
var driller1 = new DrillerWhichYouCannotChange1();
var driller2 = new DrillerWhichYouCannotChange2();
var allDrillTypes = new Dictionary<int, IList<IDriller>>()
{
{ 13, new List<IDriller>() { new DrillerWhichYouCannotChange1Adapter(driller1) } },
{ 14, new List<IDriller>() { new DrillerWhichYouCannotChange2Adapter(driller2) } },
};
Console.WriteLine(allDrillTypes[13][0].SomeCommonProperty); // prints 123
Console.WriteLine(allDrillTypes[14][0].SomeCommonProperty); // prints 456
}
interface IDriller
{
int SomeCommonProperty { get; }
}
class DrillerWhichYouCannotChange1Adapter : IDriller
{
private DrillerWhichYouCannotChange1 inner;
public DrillerWhichYouCannotChange1Adapter(DrillerWhichYouCannotChange1 inner)
{
this.inner = inner;
}
public int SomeCommonProperty { get { return this.inner.PropertyX; } }
}
class DrillerWhichYouCannotChange2Adapter : IDriller
{
private DrillerWhichYouCannotChange2 inner;
public DrillerWhichYouCannotChange2Adapter(DrillerWhichYouCannotChange2 inner)
{
this.inner = inner;
}
public int SomeCommonProperty { get { return this.inner.PropertyY; } }
}
class DrillerWhichYouCannotChange1
{
public int PropertyX { get { return 123; } }
}
class DrillerWhichYouCannotChange2
{
public int PropertyY { get { return 456; } }
}
EDIT: If you cannot change the driller classes, you could use the adapter-pattern to create one adapter per driller, which implements IDiller
.
Upvotes: 1
Reputation: 50114
Even if you can't change the class definitions, you can avoid using reflection:
// Getter dictionary rather than type dictionary.
Dictionary<int, Func<IEnumerable<object>>> DrillTypeGetters =
new Dictionary<int, Func<IEnumerable<object>>>()
{
{ 13, () => TCHEMISTRY.FindAll().Cast<object>() },
{ 14, () => TDRILLSPAN.FindAll().Cast<object>() }
};
Dictionary<int, Func<object, int>> IDGetters =
new Dictionary<int, Func<object, int>>()
{
{ 13, o => ((TCHEMISTRY)o).RunID },
{ 14, o => ((TDRILLSPAN)o).RunID }
};
IEnumerable<object> LC = DrillTypeGetters[13]();
IEnumerable<object> LingLC =
from obj in LC
where IDGetters[13](obj) == 1001
select obj;
Or you could even just switch on 13
/14
and run a completely different method per type.
if (choice == 13)
IEnumerable<TCHEMISTRY> LingLC =
TCHEMISTRY.FindAll().Where(tc => tc.RunID == 1001);
else if (choice == 14)
IEnumerable<TDRILLSPAN> LingLC =
TDRILLSPAN.FindAll().Where(td => td.RunID == 1001);
Basically, if the two classes don't share any common hierarchy, you can't write any common code to deal with them. If they have lots of similar properties, you can use getters as in my first example to provide a way to get the similar properties whatever type of class you're dealing with. If they don't even have similar properties, don't try to write shared code.
Upvotes: 3
Reputation: 2261
you need to cast LC to the to the return type of the FindAll method. Something on the lines of:
var genericList = ((List<TChemistry>) LC);
var LingLC = from obj in genericList where obj.RunID == 1001 select obj;
This is assuming that FindAll returns a collection of TChemistry.
--edit
If you do not know at runtime if the type will be TChemistry or TDrillspan, then you will have to write an if/else of switch statement to cast to the correct type. I would rather have TChemistry and TDrillSpan extend an abstract class or an interface, and you can just cast to List, and you will always have RunId property.
public abstract class TAbstract
{
public abstract int RunId {get; set;}
}
public class TChemistry : TAbstract
{
public override int RunId {get; set;}
}
public class TDrillSpan : TAbstract
{
public override int RunId {get; set;}
}
Type T = AllDrillTypes[13] as TAbstract;
var LC = Activator.CreateInstance( typeof(List<>).MakeGenericType( T ) );
MethodInfo M = T.GetMethod("FindAll", BindingFlags.Public | BindingFlags.Static, null, new Type[] { }, null);
LC = M.Invoke(null, new object[] { });
var genericList = ((List<TAbstract>) LC);
var LingLC = from obj in genericList where obj.RunID == 1001 select obj;
If you cannot change the declaration of classes, then you are only left with ugly if else:
var typeInfo = LC.GetType();
IEnumerable<T> genericList;
if (typeInfo == typeof(IEnumerable<TChemistry>)
{
genericList = (List<TChemistry>) LC;
)
else if (typeInfo == typeof(IEnumerable<TDrillSpan>)
{
genericList = (List<TDrillSpan>) LC;
}
var LingLC = from obj in genericList where obj.RunID == 1001 select obj;
Upvotes: 0