Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking for IEnumerable<T> with reflection

EDIT

The bare-bones version of this question is, if I have some object o, how would I check to see if o is of some type that implements IEnumerable<string> with reflection? The original question is much more specific, but an answer to the above would be just as good. Sorry if I gave too much detail on this question

END EDIT

The following is a contrived ValueInjecter POC. Everything works well except for the isCollectionMapping method at the very bottom. I'm trying to get it to return true if and only if both the source and target property are any object that implement IEnumerable<respectiveTypes>.

I've tried IsAssignableFrom and also IsInstanceOfType, but neither seems to work.

Everything else works since when I uncomment the second line of the method to check explicitly for properties of name "Children", it works fine.

Note - I do know there are issues with this example. Namely, I'm trying to check for any old IEnumerable<> but yet always knowing enough to return List<>; it's just a silly proof of concept at this point.

[TestClass]
public class UnitTest1 {

    [TestMethod]
    public void TestMethod1() {
        List<string> strings = new List<string>();

        Subject S = new Subject() {
            id = 1,
            SubjectName = "S1",
            Children = { new Subject() { id = 2, SubjectName = "S1a" },
                         new Subject() { id = 3, SubjectName = "S1b", Children = { new Subject() { id = 4} } } }
        };

        SubjectViewModel VM = (SubjectViewModel)new SubjectViewModel().InjectFrom<CollectionToCollection>(S); ;


        Assert.AreEqual(2, VM.Children.Count);
        Assert.AreEqual(1, VM.Children.Single(s => s.id == 3).Children.Count);
    }
}


public class Subject {
    public Subject() {
        Children = new List<Subject>();
    }

    public string SubjectName { get; set; }
    public int id { get; set; }

    public List<Subject> Children { get; set; }
}

public class SubjectViewModel {
    public SubjectViewModel() {
        Children = new List<SubjectViewModel>();
    }

    public string SubjectName { get; set; }
    public int id { get; set; }

    public List<SubjectViewModel> Children { get; set; }
}

public class CollectionToCollection : Omu.ValueInjecter.ConventionInjection {
    protected override bool Match(ConventionInfo c) {
        return c.TargetProp.Name == c.SourceProp.Name;
    }

    protected override object SetValue(ConventionInfo c) {
        if (isCollectionMapping(c))
            return (c.SourceProp.Value as IEnumerable<Subject>).Select(s => (SubjectViewModel)(new SubjectViewModel().InjectFrom<CollectionToCollection>(s))).ToList();
        else
            return c.SourceProp.Value;
    }

    private bool isCollectionMapping(ConventionInfo c) {
        return c.SourceProp.Value.GetType().IsInstanceOfType(typeof(IEnumerable<Subject>)) && c.TargetProp.Value.GetType().IsAssignableFrom(typeof(IEnumerable<SubjectViewModel>));

        //return c.SourceProp.Name == "Children" && c.TargetProp.Name == "Children";
    }
}
like image 564
Adam Rackis Avatar asked Apr 06 '11 13:04

Adam Rackis


People also ask

What is IEnumerable T?

IEnumerable. IEnumerable<T> contains a single method that you must implement when implementing this interface; GetEnumerator, which returns an IEnumerator<T> object. The returned IEnumerator<T> provides the ability to iterate through the collection by exposing a Current property.

What is IEnumerable<> in c#?

IEnumerable is an interface defining a single method GetEnumerator() that returns an IEnumerator interface. It is the base interface for all non-generic collections that can be enumerated. This works for read-only access to a collection that implements that IEnumerable can be used with a foreach statement.

Why do we need IEnumerable in c#?

IEnumerable is best to query data from in-memory collections like List, Array etc. IEnumerable doesn't support add or remove items from the list. Using IEnumerable we can find out the no of elements in the collection after iterating the collection. IEnumerable supports deferred execution.


3 Answers

If I have some object o, how would I check to see if o is of some type that implements IEnumerable<string>?

As simple as:

o is IEnumerable<string>

By the way, your current code isn't working because it is reversing the testing of the assignability relationship (as though the method were called IsAssignableTo), i.e. It is assuming that:

Bar bar = ...
Foo foo = bar

implies:

typeof(Bar).IsAssignableFrom(typeof(Foo)) // wrong

In reality, the actual implication is that:

typeof(Foo).IsAssignableFrom(typeof(Bar))

Namely, I'm trying to check for any old IEnumerable<>:

In this case, you need to test if the type implements a constructed version of the generic interface:

o.GetType()
 .GetInterfaces()
 .Any(t => t.IsGenericType 
        && t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
like image 124
Ani Avatar answered Sep 28 '22 03:09

Ani


The bare-bones version of this question is, if I have some object o, how would I check to see if o is of some type that implements IEnumerable<string>?

Like this:

object o = whatever;
bool isSequenceOfStrings = o is IEnumerable<string>;
like image 40
Eric Lippert Avatar answered Sep 28 '22 04:09

Eric Lippert


I could have missed something (didn't read your whole code sample), but it doesn't seem like you need reflection here.

How about just using:

if (c.SourceProp.Value is IEnumerable<Subject>)
   return true;

If you don't know the specific type, use generics:

public bool MyFunction<T>(...)
{
   if (c.SourceProp.Value is IEnumerable<T>)
      return true;
}

Or if you need to use the interface, do it this way (saves a cast):

var enumerable = c.SourceProp.Value as IEnumerable<string>;
if (enumerable != null)
{
   // Use IEnumerable<string>
   return true;
}

return false;
like image 29
Josh G Avatar answered Sep 28 '22 04:09

Josh G