简体   繁体   中英

How to iterate List<T> where T: MyClass

I have entity with nested list:

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; }
}

Also I have function that collects all [MyAttribute] marked properties and do something operations with them:

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 */
        }
    }
}

Questions:

  • How to compare property type with List<T> ?
  • How to iterate list if I know that it's inherited from EntityBase ?

PS

I'm using .NET 4.5

How to compare property type with List<T> ?

Correctly identifying something as a list is ... tricky; especially if you want to handle all edge-cases (a custom IList<Foo> implementation, or a class that subclasses List<T> , etc). A lot of framework code checks instead for "implements the non-generic IList , and has a non- object indexer":

    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;
    }

How to iterate list if I know that it's inherited from EntityBase ?

Assuming you mean that the items are inherited from EntityBase , and you have determined that it is a list (from the previous question), then the easiest option is IList and foreach :

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

Note: if you are going to get the value anyway , you can also reverse this and use an is test before using 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)
    {
        // ...
    }
}

How to compare property type with List<T> :

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

How to iterate list if I know that [its items are] inherited from EntityBase:

var listForIteration = (IEnumerable<EntityBase>)list;

foreach (var item in listForIteration)
{
}

A List<T> is not covariant, but (as of .NET 4.0) an IEnumerable<T> is.

UPDATE : After some comments, here's an answer to the question you didn't ask:

How to test if I can enumerate an object as a sequence of items that inherit from EntityBase, and if so, loop over it:

var listForIteration = list as IEnumerable<EntityBase>;

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

This will (on .NET 4.0 or later) allow you to iterate any collection of EntityBase (or derived) objects, even if the collection is not a List<> (except for those that don't implement IEnumerable<T> , but those are rare).

You probably want something like

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

If you only want to iterate the list, you can use IEnumerable<out T> 's covariance:

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

How to iterate list if I know that it's inherited from EntityBase?

You know it's a subclass of type EntityBase , so instead of using generics, why not just go for a good old superclass reference? Then there's no need to use generics in this scenario, when List<EntityBase> will do fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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