在非泛型接口中使用泛型集合
本文关键字:泛型 集合 泛型接口 | 更新日期: 2023-09-27 18:31:44
我今天花了很长时间寻找解决这个问题的方法,但没有运气。我有一个泛型类,如下所示:
public class StagedEntity<T>: IStagedEntity where T : IStagedItem {
public Type EntityType {
get { return typeof( T ); }
}
public IList<T> StagedItems { get; set; }
}
以便我可以创建各种任意类型的暂存实体的集合,即:
var stagedEntities = new List<IStagedEntity> {
new StagedEntity<StagedBrand> {
StagedItems = new List<StagedBrand> {
new StagedBrand {
Code = "Brand1"
},
new StagedBrand {
Code = "Brand2"
}
}
},
new StagedEntity<StagedModel> {
StagedItems = new List<StagedModel> {
new StagedModel {
Name = "Model1"
},
new StagedBrand {
Name = "Model2"
}
}
}
}
然后,我希望在 stagedEntities
中迭代每个实体,并且对于每个实体,我需要能够访问其 StagedItems
集合中每个项目的所有属性值,如下所示:
private void DoStuff( IStagedEntity stagedEntity ) {
var properties = stagedEntity.EntityType.GetProperties().ToList();
foreach ( var item in stagedEntity.StagedItems ) { <=== This line doesn't compile
foreach ( var property in properties ) {
// Do something with
// property.GetValue( item, null );
}
}
}
但是上面的代码无法编译,因为接口定义IStagedEntity
没有定义IList<T> StagedItems
,我不能将其放在接口定义中,因为接口是非通用的,因此不知道T
是什么。
我已经尝试了各种方法,例如使接口通用(IStagedEntity<T>
)和将StagedEntities
定义为new List<IStagedEntity<IStagedItem>>
,我可以通过将StagedEntity<T>
的每个实例转换为IStagedEntity<IStagedItem>
来编译它,但是这在运行时失败了:
Unable to cast object of type
'DataExport.StagedEntity`1[DataExport.StagedBrand]' to type
'DataExport.IStagedEntity`1[DataExport.IStagedItem]'.
有没有我错过的明显解决方法?任何帮助将不胜感激。
编辑:为了完整起见,目前的IStagedEntity
界面如下所示:
public interface IStagedEntity {
Type EntityType { get; }
}
如果你可以更改DoStuff()
的方法签名,我建议保持你的接口和类不变,只是像这样改变参数:
private void DoStuff<T>(StagedEntity<T> stagedEntity) // note the arg type
{
var properties = stagedEntity.EntityType.GetProperties().ToList();
foreach ( var item in stagedEntity.StagedItems ) {
foreach ( var property in properties ) {
// Do something with
// property.GetValue( item, null );
}
}
}
因此,您可以像这样调用该方法:
private void foo()
{
StagedEntity<MyGenericType> entities = null; // ...
DoStuff(entities);
}
使用动态将获得您要求的结果。另一方面,我会考虑其他建议的方法,因为您不知道是否所有 IStagedEntity 都满足 StagedItems 属性
private static void DoStuff(IStagedEntity stagedEntity)
{
dynamic se = stagedEntity;
var properties = stagedEntity.EntityType.GetProperties().ToList();
foreach (var item in se.StagedItems)
{
foreach (var property in properties)
{
// Do something with
// property.GetValue( item, null );
}
}
}
我设法通过使IStagedEntity
接口通用来使其工作,以便我可以在其中包含IList<T> StagedItems
- 我之前尝试过,但至关重要的是,我现在还制作了接口类型参数协变 ( <out T>
),这解决了我之前遇到的转换问题:
public interface IStagedEntity<out T> where T : IStagedItem {
Type EntityType { get; }
IList<T> StagedItems { get; }
}
显然,我还必须修改所有对IStagedEntity
的引用,以IStagedEntity<IStagedItem>
,并且这样做后,赋值(简化如下)现在可以毫无问题地工作:
var stagedEntities = new List<IStagedEntity<IStagedItem>> {
new StagedEntity<StagedBrand>(),
new StagedEntity<StagedModel>()
}
现在界面中的IList<T> StagedItems
,我可以愉快地以我的消费方式访问此属性。
谢谢大家的帮助:o)
编辑:可能值得指出的是,如果接口上存在带有setter的泛型属性,则如上所述添加协变out
将导致编译器抱怨"无效方差"。简单地删除二传手就可以解决这个问题(当然,你仍然可以在具体实现上拥有二传手)。