如何迭代List其中T:MyClass

本文关键字:其中 MyClass List 何迭代 迭代 | 更新日期: 2023-09-27 18:36:31

我有带有嵌套列表的实体:

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<T>进行比较?
  • 如果我知道它是从EntityBase继承的,如何迭代列表?

附言

我正在使用 .NET 4.5

如何迭代List<T>其中T:MyClass

如何比较房产类型与List<T>

正确将某些内容标识为列表是...棘手;特别是如果你想处理所有边缘情况(自定义IList<Foo>实现,或者子类List<T>类等)。许多框架代码检查"实现非泛型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继承的,如何迭代列表?

假设你的意思是这些项目是从EntityBase继承的,并且你已经确定它是一个列表(来自上一个问题),那么最简单的选择是IListforeach

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

注意:如果您无论如何都要获得该值,您也可以反转此操作并在使用GetListType之前使用is测试:

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)
    {
        // ...
    }
}

如何将属性类型与List<T>进行比较:

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

如果我知道[它的项]是从实体库继承的,如何迭代列表:

var listForIteration = (IEnumerable<EntityBase>)list;
foreach (var item in listForIteration)
{
}

List<T>不是协变的,但(从 .NET 4.0 开始)IEnumerable<T>是协变的。

更新:经过一些评论,以下是您没有问的问题的答案:

如何测试我是否可以将对象枚举为从 EntityBase 继承的一系列项,如果是,则循环遍历它:

var listForIteration = list as IEnumerable<EntityBase>;
if (listForIteration != null)
{
    foreach (var item in listForIteration)
    {
    }
}

这将(在 .NET 4.0 或更高版本上)允许您迭代 EntityBase(或派生)对象的任何集合,即使该集合不是List<>(不包括那些未实现 IEnumerable<T> 的对象,但这种情况很少见)。

你可能想要类似的东西

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

如果只想迭代列表,可以使用 IEnumerable<out T> 的协方差:

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

如果我知道列表是从实体库继承的,如何迭代列表?

你知道它是类型 EntityBase 的子类,所以与其使用泛型,为什么不直接去找一个好的旧超类引用呢?那么在这种情况下就不需要使用泛型,当List<EntityBase>可以正常工作时。