我有一个词典:
Dictionary<int, Type> AllDrillTypes = new Dictionary<int, Type>()
{
{13,typeof(TCHEMISTRY)},
{14,typeof(TDRILLSPAN)}
};
其中TCHEMISTRY和TDRILLSPAN是类。然后我想从这些类中获取行,如下所示:
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[] { });
所有这些代码都能正常运行。之后我需要得到这样的行:
var LingLC = from obj in LC where obj.RunID == 1001 select obj;
但这一行会导致错误:
“无法找到源类型的查询模式的实现 '宾语'。 “哪里找不到。”
此代码行出了什么问题?
答案 0 :(得分:3)
即使您无法更改类定义,也可以避免使用反射:
// 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;
或者您甚至可以启用13
/ 14
并为每种类型运行完全不同的方法。
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);
基本上,如果这两个类不共享任何公共层次结构,则不能编写任何公共代码来处理它们。如果他们有很多相似的属性,你可以像我在第一个例子中那样使用getter来提供一种获取类似属性的方法,无论你正在处理什么类型的类。如果他们甚至没有类似的属性,请不要尝试编写共享代码。
答案 1 :(得分:1)
也许你可以将你的代码改写成这样的东西,......以获得更加类型安全的解决方案(不使用反射)。
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; } }
}
编辑:如果您无法更改司钻类,则可以使用适配器模式为每个司机创建一个适配器,该适配器实现IDiller
。
答案 2 :(得分:0)
您需要将LC转换为FindAll方法的返回类型。有点像:
var genericList = ((List<TChemistry>) LC);
var LingLC = from obj in genericList where obj.RunID == 1001 select obj;
这假设FindAll返回一个TChemistry的集合。
- 修改
如果您在运行时不知道类型是TChemistry还是TDrillspan,那么您必须编写if / else of switch语句以转换为正确的类型。我宁愿让TChemistry和TDrillSpan扩展一个抽象类或接口,你只需要转换为List,你将永远拥有RunId属性。
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 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;
答案 3 :(得分:0)
尝试
IEnumerable returnedObjects = (IEnumerable)M.Invoke(null, new object[] { }) as IEnumerable;
然后遍历你的可数字
foreach (object report in returnedObjects)
{
// Use reflection to read properties or add to a new List<object> if you
// need an ICollection<object>
}
而不是:
LC = M.Invoke(null, new object[] { });