Checking for IEnumerable<T> with reflection
Asked Answered
S

3

16

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";
    }
}
Sufism answered 6/4, 2011 at 13:59 Comment(0)
Z
34

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<>))
Zygotene answered 6/4, 2011 at 14:19 Comment(1)
Be careful to remember that string type is IEnumerableEpiphenomenon
S
10

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>;
Sturgis answered 6/4, 2011 at 14:17 Comment(8)
I seem to really have a hard time striking a balance between too much information, and too little. I need to do it with reflection. Sorry for the confusion, Eric. :)Sufism
@Adam: Then what is the question? Is the question "If I have some Type object t how would I check to see if t implements IEnumerable<string>?" Or is the question "If I have some type object t then how would I check if t is IEnumerable<string>?" Or is the question "If I have some type object t then how would I check if T is convertible to IEnumerable<string??" or "... is equal to IEnumerable<T> for any T?" or... Those are all different questions. You'll get the answer you want when you ask the question you actually are interested in .Sturgis
Why do you need to do it with reflection?Caracul
The entire question—after my edit—lists out the situation, and why I need reflection. Upon reading the question back to myself, I thought it may have been a bit long given how simple the problem was, so I added the edit at the beginning. I ended up making things more confusing, not less. Edit-fail.Sufism
When I look at the code sample, I see you testing c.SourceProp.Value against a literal interface implementation (IEnumerable<Subject>). This doesn't seem to require reflection. c.SourceProp.Value is IEnumerable<Subject> returns the same result, does it not?Caracul
If you are using unknown types (the usual case for reflection), you could still chose to wrap these in a nested class of a generic type, which could allow you to avoid reflection.Caracul
@Adam: Looking at your code, I don't understand why you feel that you need to use reflection. What is wrong with "(c.SourceProp.Value is IEnumerable<Subject>) && (c.TargetProp.Value is IEnumerable<SubjectViewModel>)" ???Sturgis
Nothing. I was wrong to assume I needed reflection. I guess we've come full circle, and my edit, and your answer turned out to be accurate. I hope this question doesn't reflect how the rest of this day will go :-\Sufism
C
4

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;
Caracul answered 6/4, 2011 at 14:19 Comment(1)
Thank you, yes, the simple is would have worked. Not sure how I missed that. +1Sufism

© 2022 - 2024 — McMap. All rights reserved.