有一个非常有趣的情况,似乎无法在反射,继承和泛型的网络上找到任何具体的东西。
首先:
我有一个基础抽象分类,名为:
public abstract class ItemBase
{
// default properties
}
ItemBase然后成为我的超类到名为Item的抽象实体:
public abstract class Item
{
// some extra properties
}
这背后的想法是构造将继承自Item的实体,并设置其他属性:
public partial class City : Item {}
现在,在一个测试方法中,我试图使用反射来调用传递的实体上的方法,并从方法中返回任何形式的数据。
假设为了测试目的,我们在State中有一个方法,它返回所有主要城市的集合:
public List<City> GetAllMajorCities() {}
在我的存根中,我可能需要测试才能获得所有城市:
List<Item> cities = this.InvokeGenericMethod<Item>("State", "GetAllMajorCities", args);
然后InvokeGenericMethod(string,string,object [] args)看起来像:
public IEnumerable<T> InvokeGenericMethod<Item>(string className, string methodName, object[] args)
{
Type classType = className.GetType(); // to get the class to create an instance of
object classObj = Activator.CreateInstance(classType);
object ret = classType.InvokeMember(methodName, BindingFlags.InvokeMethod, null, classObj, args);
}
理论上,从给出的示例中,“ret”的值应该是IEnumerable的一种类型,但是,如果我将ret值检查为:<返回类型,如果添加到监视器中,则返回List,如果: / p>
if (ret is IEnumerable<T>)
它忽略了支票。
如果我改变
public List<City> GetAllMajorCities() {}
到
public List<ItemBase> GetAllMajorCities() {}
它似乎坚持继承并允许我将其转换为IEnumberable。
我完全被难住了。
任何想法,建议都将不胜感激。
谢谢,
埃里克
答案 0 :(得分:3)
您是否可以使用Framework 3.5?在.net 4.0中引入了通用协同和逆变。因此,框架3.5中无法实现以下目标:
class ItemBase { }
class Item : ItemBase { }
class City : Item { }
class Program
{
static void Main(string[] args)
{
IEnumerable<City> cities = new List<City>();
IEnumerable<Item> items = cities; // Compile error here. IEnumerable<Item> is not a supertype of IEnumerable<City>.
}
}
请注意,以下(您尝试实现的目标)甚至无法在.net 4.0中使用:
List<Item> cities = new List<City>();
为什么不起作用?因为必须可以将Item
添加到List<Item>
。但是,显然cities.Add(new Person());
无效(因为cities
的“真实”(=静态)类型为List<City>
)。因此,List<Item>
永远不能是List<City>
的超类型。
您无法将项目添加到IEnumerable,这就是 IEnumerable<City>
和IEnumerable<Item>
之间的子类型关系的原因(但仅限于.net 4.0,如上所述)
答案 1 :(得分:1)
您可以尝试这样的事情:
Type type = ret.GetType();
Type listType = typeof(IEnumerable<T>);
if (type == listType || type.IsSubclassOf(listType))
{
//
}
答案 2 :(得分:1)
首先,这是错误的:
Type classType = className.GetType();
此构造将生成className
的{{1}}类型,但不会生成名称存储在String
中的类型。
更好,更类型安全的方式是:
className
更新:如果在调用public IEnumerable<TItem> InvokeGenericMethod<TItem, TContainer>(...)
where TItem : ItemBase
where TContainer : class, new()
{
TContainer container = new TContainer();
...
}
的地方静态地不知道TContainer
类型,请查看静态 Type.GetType
方法从其字符串名称中解析实际名称。在最简单的情况下,Type.GetType(String)
方法访问您的类型的程序集限定名称,例如InvokeGenericMethod
。