PhD
PhD

Reputation: 11334

How do I check if my uninitialized variable is of type List?

I have the following class definition:

[ActionsClass[typeof(MyActions)]
public class NkProject
    {
        int ProjectId;
        IList<NkItem> Items;
        NkItem SingleItem;
    }

The class mostly contains only fields, with the class attribute stating which class is responsible for "handling" this class as part of a larger framework.

Using reflection, I parse this file to generate some meta data and I'd like to know if "Items" is of type List (or any type of collection which acts as a list) but everything I've tried so far fails. I'm iterating over an array of FieldInfo[] objects.

  1. Check for IEnumerable:

    
    if(field.FieldType.GetInterface("IEnumerable") != null)
    
    

    This also detects strings as IEnumerables, which is incorrect. I'd prefer if strings fail to pass this check and get treated as primitives in a sense (the else of the above if).

  2. Changed above to: if(field is ICollection) and that also fails.

  3. Changed above to: if(field is IList && field.GetType().IsGenericType) and it still fails to be detected correctly.

Is there a way to look only at the type of the variable references to ascertain whether Items is of type List and a parallel check for string Items fails? I'm guessing #2 and #3 above fail because Items is not an object and thus there isn't any inheritance information attached to it. I could be wrong, but I think that's what is happening. A field.GetType().Name returns the type as IList`1. Not sure how to get around this.

EDIT/UPDATE: If the above check fails, and a string goes through, the output meta is incorrectly formatted. Since, if it's a collection/list of items, I need to fetch further metadata for them (via the appropriately set attributes). This step is only executed if the variable is of type List/Collection but not if it's a single reference or a primitive.

Reflection code:


foreach (var field in fieldInfo)
{
    Property property;

    //If it's a collection field, get the appropriate collection name as set in its attribute
    if (field.FieldType.GetInterface("IEnumerable") != null) //currently lets strings pass through too.
    {
        //Get the type of the "type argument" of the IEnumerable
        Type[] typeArgs = field.FieldType.GetGenericArguments();
        //Get the collection name for that type to use in the XML
        MyAttribute att = (MyAttribute )Attribute.GetCustomAttribute(typeArgs[0], typeof(MyAttribute ));

        if (att == null)
            throw new ArgumentException("Using collection: {0} in Your class: {1}. Collection must have MyAttribute[Collection=\"\"] attribute defined on its class declaration.");

        else
        {   
         //do something with meta data          
        }
    }

    //If non-collection, possibly primitive field do something else
    else
    {
        //do other stuff here.
    }
}

Upvotes: 1

Views: 269

Answers (2)

msmolcic
msmolcic

Reputation: 6577

You can try this method:

public bool IsGenericList(Type type)
{
    if (type == null)
        return false;

    if (!type.IsGenericType)
        return false;

    var genericArguments = type.GetGenericArguments();
    if (genericArguments.Length != 1)
        return false;

    var listType = typeof(IList<>).MakeGenericType(genericArguments);
    return listType.IsAssignableFrom(type);
}

Upvotes: 1

Alistair
Alistair

Reputation: 1144

You can use the generic type information to check whether it is a List

if(field.FieldType.IsGenericType 
    && field.FieldType.GetGenericTypeDefinition() == typeof(List<>))

currently I have the following function in my code base for this type of situation.

public static bool Closes(this Type type, Type openType)
{
    if (type == null)
        return false;

    if (type.IsGenericType && type.GetGenericTypeDefinition() == openType) return true;

    foreach (var @interface in type.GetInterfaces())
    {
        if (@interface.Closes(openType)) return true;
    }

    return type.BaseType.Closes(openType);
}

this would allow you to just call

field.FieldType.Closes(typeof(List<>)

Upvotes: 3

Related Questions