Проверка наличия IEnumerable с отражением

#c# #reflection #valueinjecter

#c# #отражение #valueinjector

Вопрос:

Редактировать

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

ЗАВЕРШИТЬ РЕДАКТИРОВАНИЕ

Ниже приведен надуманный запрос значения 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>)) amp;amp; c.TargetProp.Value.GetType().IsAssignableFrom(typeof(IEnumerable<SubjectViewModel>));

        //return c.SourceProp.Name == "Children" amp;amp; 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 
        amp;amp; t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
  

Комментарии:

1. Будьте осторожны и помните, что строковый тип — IEnumerable

Ответ №2:

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

Вот так:

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

Комментарии:

1. Кажется, мне действительно трудно найти баланс между слишком большим количеством информации и слишком малым. Мне нужно сделать это с отражением. Извини за путаницу, Эрик. 🙂

2. @Adam: Тогда в чем вопрос? Возникает вопрос: «Если у меня есть объект некоторого типа t, как бы я проверил, реализует ли t IEnumerable<string>?» Или вопрос «Если у меня есть объект некоторого типа t, то как мне проверить, является ли t IEnumerable<string>?» Или вопрос «Если у меня есть объект некоторого типа t, то как мне проверить, преобразуется ли T в IEnumerable<string??» или «… равно IEnumerable<T> для любого T?» или… Это все разные вопросы. Вы получите желаемый ответ, когда зададите вопрос, который вас действительно интересует .

3. Почему вам нужно делать это с отражением?

4. Когда я смотрю на пример кода, я вижу, что вы тестируете c.SourceProp.Value на соответствие реализации буквального интерфейса (IEnumerable<Subject>). Похоже, это не требует отражения. c.SourceProp.Value — IEnumerable<Subject> возвращает тот же результат, не так ли?

5. @Adam: Глядя на ваш код, я не понимаю, почему вы считаете, что вам нужно использовать reflection. Что не так с «(c.SourceProp.Value — IEnumerable<Subject>) amp;amp; (c.TargetProp.Value — IEnumerable<SubjectViewModel>)»???

Ответ №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;
  

Комментарии:

1. Спасибо, да, простое is сработало бы. Не уверен, как я это пропустил. 1