简体   繁体   English

在非泛型接口中使用泛型集合

[英]Use of Generic Collection in Non-Generic Interface

I've spent quite a while today looking for a way to solve this but with no luck. 今天,我花了相当长的时间寻找解决该问题的方法,但是没有运气。 I have a generic class as follows: 我有如下通用类:

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

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

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

in order that I can create a collection of StagedEntities of various arbitrary types, ie: 为了可以创建各种任意类型的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"
      }
    }
  }
}

I then wish to iterate over each entity in stagedEntities , and with each one I need to be able to access all of the property values of each item in its StagedItems collection, something like: 然后,我希望遍历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 );
    }
  }
}

However the above code doesn't compile because the interface definition IStagedEntity doesn't have IList<T> StagedItems defined, I can't put this in the interface definition because the interface is non-generic and so doesn't know what T is. 但是上面的代码无法编译,因为接口定义IStagedEntity没有定义IList<T> StagedItems ,我不能将其放在接口定义中,因为接口是非泛型的,所以不知道T是什么。

I've tried various things such as making the interface generic ( IStagedEntity<T> ) and defining StagedEntities as new List<IStagedEntity<IStagedItem>> , and I can get this to compile by casting each instance of StagedEntity<T> to IStagedEntity<IStagedItem> , however this fails at runtime with: 我已经尝试了各种方法,例如使接口通用( IStagedEntity<T> )并将StagedEntities定义为new List<IStagedEntity<IStagedItem>> ,并且可以通过将StagedEntity<T>每个实例StagedEntity<T>IStagedEntity<IStagedItem> ,但是在运行时失败并显示:

Unable to cast object of type
'DataExport.StagedEntity`1[DataExport.StagedBrand]' to type
'DataExport.IStagedEntity`1[DataExport.IStagedItem]'.

Is there an obvious way around this that I'm missing? 有什么办法可以解决我所缺少的吗? Any help would be much appreciated. 任何帮助将非常感激。


EDIT: For completeness, the IStagedEntity interface at present looks like this: 编辑:为了完整IStagedEntity ,当前的IStagedEntity接口看起来像这样:

public interface IStagedEntity {
  Type EntityType { get; }
}

If you can change the method signature of DoStuff() , I'd suggest to keep your interfaces and classes as they are and just change the argument like this: 如果可以更改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 );
    }
  }
}

So you can call that method like: 因此,您可以像这样调用该方法:

private void foo()
{
    StagedEntity<MyGenericType> entities = null; // ...
    DoStuff(entities);
}

Using dynamic will get you the result that you are asking for. 使用动态将获得所需的结果。 I would on the other side consider the other suggested approaches since you do not know if all IStagedEntity will meet the StagedItems property 另一方面,我将考虑其他建议的方法,因为您不知道所有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 );
            }
        }
}

I've managed to get this to work by making the IStagedEntity interface generic so I can include IList<T> StagedItems in it - which I tried previously, but crucially I've now also made the interface type parameter covariant ( <out T> ) which has solved the conversion issue I was getting before: 我设法通过使IStagedEntity接口通用来使它IStagedEntity因此我可以在其中包括IList<T> StagedItems我之前曾尝试过,但很关键的是,现在我还使接口类型参数为协变( <out T> )已解决了我之前遇到的转换问题:

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

  IList<T> StagedItems { get; }
}

Obviously I've also had to amend all references to IStagedEntity to IStagedEntity<IStagedItem> , and having done so the assignment (simplified below) now works without issue: 显然,我还必须IStagedEntity IStagedEntity<IStagedItem>所有引用都修改为IStagedEntity<IStagedItem> ,并且这样分配(现在简化了)现在可以正常工作了:

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

And with IList<T> StagedItems now in the interface I can happily access this property in my consuming method. 现在,在接口中有了IList<T> StagedItems ,我可以很高兴地在使用方法中访问此属性。

Thanks for your help everyone :o) 谢谢大家的帮助:o)


EDIT: It's possibly worth pointing out that adding the covariant out as described above will cause the compiler to complain of 'invalid variance' if there is a generic property with a setter on the interface. 编辑:这可能是值得指出的是,加入协变out如上所述将导致编译器抱怨“无效的差异”,如果有与接口上的一个设置的通用属性。 Simply removing the setter will fix this (and of course you can still have setters on the concrete implementation). 只需删除设置器即可解决此问题(当然,您仍然可以在具体实现中使用设置器)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM