Как я могу использовать отражение для поиска свойств, реализующих определенный интерфейс?

Рассмотрим следующий пример:

public interface IAnimal
{
}

public class Cat: IAnimal
{
}

public class DoStuff
{
    private Object catList = new List<Cat>();

    public void Go()
    {
        // I want to do this, but using reflection instead:
        if (catList is IEnumerable<IAnimal>)
            MessageBox.Show("animal list found");

        // now to try and do the above using reflection...
        PropertyInfo[] properties = this.GetType().GetProperties();
        foreach (PropertyInfo property in properties)
        {
            //... what do I do here?
            // if (*something*)
                MessageBox.Show("animal list found");
        }
    }
}

Можете ли вы завершить оператор if, заменив что-то правильным кодом?

EDIT:

Я заметил, что я должен был использовать свойство вместо поля, чтобы это работало, поэтому оно должно быть:

    public Object catList
    {
        get
        {
          return new List<Cat>();
        }
    }

Ответ 1

Вы можете посмотреть свойства 'PropertyType, а затем использовать IsAssignableFrom, который я предполагаю, это то, что вы хотите:

PropertyInfo[] properties = this.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
    if (typeof(IEnumerable<IAnimal>).IsAssignableFrom(property.PropertyType))
    {
        // Found a property that is an IEnumerable<IAnimal>
    }                           
}

Конечно, вам нужно добавить свойство в свой класс, если вы хотите, чтобы приведенный выше код работал; -)

Ответ 2

Обратите внимание, что в вашем примере catList не будет найден с помощью GetType().GetProperties (). Вместо этого вы использовали бы GetType().GetFields ().

Если вы пытаетесь определить, определено ли свойство как IEnumerable, вы можете сделать это:

if (typeof(IEnumerable<IAnimal>) == property.PropertyType)
{
   MessageBox.Show("animal list found");
}

Если вы хотите узнать, можете ли вы присвоить значение свойства в IEnumerable<IAnimal>, выполните следующее:

if (typeof(IEnumerable<IAnimal>).IsAssignableFrom (property.PropertyType))
{
   MessageBox.Show("animal list found");
}

Если тип свойства недостаточно специфичен (например, object Animal{get;set;}), чтобы получить ответ, вам нужно будет принять значение. Вы можете сделать это:

object value = property.GetValue(this, null);
if (value is IEnumerable<IAnimal>)
{
   MessageBox.Show("animal list found");
}

Ответ 3

Другой способ сделать это - просто вызвать GetProperties() на интерфейс изнутри объекта, противоположный самому объекту.

public static void DisplayObjectInterface(object source, Type InterfaceName)
{
    // Get the interface we are interested in
    var Interface = source.GetType().GetInterface(InterfaceName.Name);
    if (Interface != null)
    {
        // Get the properties from the interface, instead of our source.
        var propertyList = Interface.GetProperties();
        foreach (var property in propertyList)
            Debug.Log(InterfaceName.Name + " : " + property.Name + "Value " + property.GetValue(source, null));
    }
    else
        Debug.Log("Warning: Interface does not belong to object.");

}

Мне нравится делать InterfaceName параметр a Type, чтобы избежать ошибок при печати GetInterface() по имени строки.

Использование:

DisplayObjectInterface(Obj, typeof(InterFaceNameGoesHere));

РЕДАКТИРОВАТЬ: я только заметил, что ваш пример представляет собой коллекцию, это не будет работать с коллекцией, переданной в целом. Вам нужно будет передать каждый элемент по отдельности. У меня возникает соблазн удалить, но это, вероятно, поможет другим, что google тот же вопрос ищет решение, которое не является коллекцией.