Проверка на IEnumerable <T> с отражением

EDIT

Голосовая версия этого вопроса есть, если у меня есть object o, как бы я проверил, есть ли o какой-то тип, который реализует IEnumerable<string> с отражением? Оригинальный вопрос гораздо более конкретный, но ответ на вышеизложенное будет таким же хорошим. Извините, если я дал слишком много подробностей по этому вопросу.

END EDIT

Ниже приведено изобретенное значение ValueInjecter POC. Все работает хорошо, за исключением метода isCollectionMapping в самом низу. Я пытаюсь заставить его возвращать true тогда и только тогда, когда и исходное, и целевое свойство - это любой объект, реализующий IEnumerable<respectiveTypes>.

Я пробовал IsAssignableFrom, а также IsInstanceOfType, но, похоже, не работает.

Все остальное работает с тех пор, как я раскомментировал вторую строку метода, чтобы явно проверить свойства имени "Дети", он отлично работает.

Примечание. Я знаю, что в этом примере есть проблемы. А именно, я пытаюсь проверить любой старый IEnumerable<>, но все же всегда достаточно знаю, чтобы возвращать List<>; это просто глупое доказательство концепции на данный момент.

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

Ответ 1

Если у меня есть какой-то объект o, как бы я проверьте, есть ли o какой-либо тип, который реализует IEnumerable<string>?

Проще, чем:

o is IEnumerable<string>

Кстати, ваш текущий код не работает, потому что он реверсирует тестирование отношения назначения (как если бы метод назывался IsAssignableTo), т.е. предполагается, что:

Bar bar = ...
Foo foo = bar

означает:

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

В действительности, фактическая импликация такова:

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

А именно, я пытаюсь проверить любой старый IEnumerable<>:

В этом случае вам нужно проверить, реализует ли тип построенной версии общего интерфейса:

o.GetType()
 .GetInterfaces()
 .Any(t => t.IsGenericType 
        && t.GetGenericTypeDefinition() == typeof(IEnumerable<>))

Ответ 2

Голосовая версия этого вопроса есть, если у меня есть некоторый объект o, как бы я проверил, есть ли какой-либо тип o, который реализует IEnumerable<string>?

Вот так:

object o = whatever;
bool isSequenceOfStrings = o is IEnumerable<string>;

Ответ 3

Я мог бы пропустить что-то (не читал весь образец кода), но похоже, что вам не нужно размышлять здесь.

Как просто использовать:

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

Если вы не знаете определенный тип, используйте generics:

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

Или, если вам нужно использовать интерфейс, сделайте это так (сохраняйте приведение):

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

return false;