【问题标题】:How to iterate List<T> where T: MyClass如何迭代 List<T> where T: MyClass
【发布时间】:2013-10-31 01:40:40
【问题描述】:

我有嵌套列表的实体:

public class Article : MyEntityBase
{
    public Article()
    {
        Tags = new List<Tag>();
    }

    [MyAttribute]
    public string Title { get; set; }

    [MyAttribute]
    public virtual List<Tag> Tags { get; set; }
}

public class Tag : EntityBase
{
    public string Title { get; set; }
}

public abstract class MyEntityBase
{
    public Guid Id { get; set; }
}

我还有收集所有[MyAttribute] 标记的属性并对它们进行操作的函数:

public function OperateWithAttributes(IEnumerable<PropertyInfo> properties)
{
    foreach (var p in properties)
    {
        if (p.PropertyType == typeof(string))
        {
            // do something
        }
        else if (/* there are code that check property type is List<T> */)
        {
            /* there are code that iterate list */
        }
    }
}

问题:

  • 如何比较属性类型和List&lt;T&gt;
  • 如果我知道列表继承自 EntityBase,如何迭代它?

附言

我正在使用 .NET 4.5

【问题讨论】:

标签: c# list loops


【解决方案1】:

如果我知道它是从 EntityBase 继承的,如何迭代列表?

你知道它是EntityBase 类型的子类,所以不要使用泛型,为什么不去找一个好的旧的超类参考呢?那么在这种情况下就不需要使用泛型了,List&lt;EntityBase&gt; 就可以了。

【讨论】:

  • @jon 该转换不起作用,因为List&lt;T&gt; 不是协变的。 .NET 中的类不能是协变的,但接口可以。看我的回答。
  • 无法投射“System.Collections.Generic.List1[Tag]" to type "System.Collections.Generic.List1[EntityBase]”。
  • @KrisVandermotten: ...这就是我发表评论的原因。
【解决方案2】:

你可能想要类似的东西

Type type = p.PropertyType;
if(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>) &&
   typeof(EntityBase).IsAssignableFrom(type.GetGenericArguments()[0]))
{
    // iterate list
}

【讨论】:

  • type.GetGenericArguments()[0] is EntityBase 错误,应该是 typeof(EntityBase).IsAssignableFrom(type.GetGenericArguments()[0])
【解决方案3】:

如何比较属性类型与List&lt;T&gt;:

if (p.PropertyType.IsGenericType && 
    p.PropertyType.GetGenericTypeDefinition() == typeof(List<>))

如果我知道 [其项目是] 从 EntityBase 继承的,如何迭代列表:

var listForIteration = (IEnumerable<EntityBase>)list;

foreach (var item in listForIteration)
{
}

List&lt;T&gt; 不是协变的,但(从 .NET 4.0 开始)IEnumerable&lt;T&gt; 是。

更新:在一些 cmet 之后,这里是你没有问的问题的答案:

如何测试我是否可以将一个对象枚举为从 EntityBase 继承的一系列项目,如果可以,则对其进行循环:

var listForIteration = list as IEnumerable<EntityBase>;

if (listForIteration != null)
{
    foreach (var item in listForIteration)
    {
    }
}

这将(在 .NET 4.0 或更高版本上)允许您迭代 EntityBase(或派生)对象的任何集合,即使该集合不是 List&lt;&gt;(那些不实现 IEnumerable&lt;T&gt; 的除外,但这些很少见)。

【讨论】:

  • "but an IEnumerable&lt;T&gt; is" - 仅在 .NET 4 中。OP 可能正在使用 .NET 4,但值得为其他阅读本文的人指出。
  • “它是从 EntityBase 继承的”应该是“它的项目从 EntityBase 继承”:)
  • IEnumerable&lt;T&gt; 是否协变取决于框架版本;此外,它可能或可能不重要,但请注意,这不能处理 public class MyCustomList : List&lt;Foo&gt;public class MyEventMoreCustomList : IList&lt;Bar&gt; 之类的事情
  • @BartoszKP 仅逐字引用 OP 的问题
  • @MarcGravell Covariance 确实适用于您提到的情况。它只需要该类实现IEnumerable&lt;T&gt;,即使它位于基类中。
【解决方案4】:

如何比较属性类型和List&lt;T&gt;

正确地将某些内容识别为列表是……棘手的;特别是如果您想处理所有边缘情况(自定义IList&lt;Foo&gt; 实现,或子类List&lt;T&gt; 等)。很多框架代码会检查“实现非通用IList,并具有非object 索引器”:

    static Type GetListType(Type type)
    {
        if (type == null) return null;
        if (!typeof(IList).IsAssignableFrom(type)) return null;

        var indexer = type.GetProperty("Item", new[] { typeof(int) });
        if (indexer == null || indexer.PropertyType == typeof(object))
            return null;

        return indexer.PropertyType;
    }

如果我知道它是从EntityBase 继承的,如何迭代列表?

假设您的意思是 items 继承自 EntityBase,并且您已经确定它是一个列表(来自上一个问题),那么最简单的选项是 IList 和 @987654330 @:

var itemType = GetListType(p.PropertyType);
if(itemType != null && itemType.IsSubclassOf(typeof(EntityBase)))
{
    var list = (IList) p.GetValue(obj);
    foreach(EntityBase item in list)
    {
        // ...
    }
}

注意:如果您要获得值无论如何,您也可以将其反转并使用is 测试之前 使用GetListType

var value = p.GetValue(obj);
Type itemType;
if(value is IList && (itemType = GetListType(p.PropertyType) != null)
      && itemType.IsSubclassOf(typeof(EntityBase)))
{
    var list = (IList)value;
    foreach(EntityBase item in list)
    {
        // ...
    }
}

【讨论】:

    【解决方案5】:

    如果只想迭代列表,可以使用IEnumerable&lt;out T&gt;的协方差:

    if (typeof(IEnumerable<EntityBase>).IsAssignableFrom(p.PropertyType))
    {
        var enumerable = (IEnumerable<EntityBase>)p.GetValue(obj);
        foreach (var entity in entities)
        {
            // do something
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-06-25
      • 1970-01-01
      • 2012-01-11
      • 2021-05-27
      • 2020-01-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多