在非通用接口中使用通用集合

时间:2016-01-25 15:02:07

标签: c# generics

我今天花了很长时间寻找解决方法,但没有运气。我有一个泛型类如下:

public class StagedEntity<T>: IStagedEntity where T : IStagedItem {

  public Type EntityType {
    get { return typeof( T ); }
  }

  public IList<T> StagedItems { get; set; }
}

为了能创建各种任意类型的StagedEntities集合,即:

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集合中每个项目的所有属性值,例如:< / p>

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>>,我可以通过转换每个{{1}实例来编译它。 1}}到StagedEntity<T>,但这在运行时失败:

IStagedEntity<IStagedItem>

有没有明显的方法可以解决这个问题?任何帮助将不胜感激。

编辑:为了完整性,目前Unable to cast object of type 'DataExport.StagedEntity`1[DataExport.StagedBrand]' to type 'DataExport.IStagedEntity`1[DataExport.IStagedItem]'. 界面如下所示:

IStagedEntity

3 个答案:

答案 0 :(得分:0)

如果你可以改变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);
}

答案 1 :(得分:0)

使用动态将获得您要求的结果。我会在另一方面考虑其他建议的方法,因为您不知道所有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 );
            }
        }
}

答案 2 :(得分:0)

我设法通过将IStagedEntity接口设为通用来实现此功能,因此我可以在其中包含IList<T> StagedItems - 我之前尝试过,但至关重要的是我现在还制作了接口类型参数协变(<out T>)解决了我之前遇到的转换问题:

public interface IStagedEntity<out T> where T : IStagedItem {
  Type EntityType { get; }

  IList<T> StagedItems { get; }
}

显然,我还必须修改IStagedEntityIStagedEntity<IStagedItem>的所有引用,并且在完成此任务后(下面简化)现在可以正常运行:

var stagedEntities = new List<IStagedEntity<IStagedItem>> {
  new StagedEntity<StagedBrand>(),
  new StagedEntity<StagedModel>()
}

现在在界面中IList<T> StagedItems,我可以愉快地在我的消费方法中访问此属性。

感谢大家的帮助:o)

编辑:可能值得指出的是,如果在接口上存在带有setter的泛型属性,则如上所述添加协变out将导致编译器抱怨'无效方差'。简单地删除setter就可以解决这个问题(当然,你仍然可以在具体实现上设置setter)。