Невозможно поместить объекты подкласса в список

Не могли бы вы помочь мне составить метод, который будет создавать список объектов.

  1. У меня есть класс с 3 подклассами.
  2. Я создал метод, который помещает все подклассы в список.
  3. Теперь у меня проблема с созданием списка, который будет содержать объекты - по одному на подкласс из шага 2.
  4. Обратите внимание, что список должен иметь тип родительского класса, потому что он будет ссылаться на объекты подклассов полиморфно.

Уточнение: я хотел бы иметь список, похожий на

List<Class01> list = new List<Class01>();
list.Add(new SubClass0101());
list.Add(new SubClass0102());
list.Add(new SubClass0103());

но он должен автоматически увеличиваться при увеличении количества подклассов.

Вопрос: мне нужна помощь с private static IEnumerable<Type> PopulateListWithObjects<TClass>()

Пожалуйста, смотрите код ниже для более подробной информации.

using System;
using System.Collections.Generic;
using System.Linq;

namespace PutObjectsInList
{
    class Program
    {
        static void Main(string[] args)
        {
            // Puts all subclasses of Class01 into the list
            var listOfSubClasses = SubClasses<Class01>();
            // Verifies that listOfSubClasses is populated with the subclasses names
            foreach (var listOfSubClass in listOfSubClasses) {Console.WriteLine(listOfSubClass);}

            var listWithObjects = PopulateListWithObjects<Class01>();

        }

        //
        private static IEnumerable<TClass> PopulateListWithObjects<TClass>()
        {
            var listWithObjects = new List<TClass>();
            // Need help with the procedure which would populate list with the
            // objects of the subclasses of the parent Class01

            return listWithObjects;
        }

        // Enumerates all subclasses for the specified class
        private static IEnumerable<Type> SubClasses<TClass>()
        {
            var subclasses =
                (from assembly in AppDomain.CurrentDomain.GetAssemblies()
                 from type in assembly.GetTypes()
                 where type.IsSubclassOf(typeof(TClass))
                 select type).ToList();
            return subclasses;
        }
    }

    public class Class01 {}
    public class SubClass0101 : Class01 {}
    public class SubClass0102 : Class01 {}
    public class SubClass0103 : Class01 {}
}

person Tracey23    schedule 20.08.2014    source источник
comment
Какой результат вы хотите, список, скажем, {new SubClass0101(), new SubClass0102(), new SubClass0103()}? Это для класса или для этого есть цель?   -  person John Saunders    schedule 20.08.2014
comment
Я хотел бы иметь список, похожий на List‹Class01› list = new List‹Class01›(); list.Add(новый SubClass0101()); list.Add(новый SubClass0102()); list.Add(новый SubClass0103());   -  person Tracey23    schedule 20.08.2014
comment
Да, это то, что я написал. Что ты собираешься делать с этим списком?   -  person John Saunders    schedule 20.08.2014
comment
Члены этого списка будут объектами типа подклассов. Затем им будут назначены их свойства. После этого базовый класс сообщит имя объекта типа подкласса и его поля.   -  person Tracey23    schedule 20.08.2014
comment
Джон, большое спасибо за продолжение! Высоко ценим вашу помощь!   -  person Tracey23    schedule 20.08.2014
comment
Опять же, какова цель этого? Это классное задание?   -  person John Saunders    schedule 20.08.2014


Ответы (2)


Ваш private static IEnumerable<Type> PopulateListWithObjects<TClass> должен иметь тип возвращаемого значения IEnumerable<TClass>, если вы хотите достичь желаемого результата.

Потому что Type обозначает не объект какого-то типа, а их тип - идентификатор метаданных System.Type.

Для создания (экземпляра) вашего экземпляра вы можете использовать Activator .CreateInstance().

В итоге это будет выглядеть так:

    private static IEnumerable<TClass> PopulateListWithObjects<TClass>()
    {
        return ReflectorHelper.GetAndActivateAllAssignableTo<TClass>();
    }

С GetAndActivateAllAssignableTo, определенным как:

 public static class ReflectorHelper
{

    /// <summary>
    /// Fetches and prepares for initialization without any constructor parameters all types in the current application domain which are assignable to T.
    /// </summary>
    /// <typeparam name="T">The type to which all desired types should be assignable to.</typeparam>
    /// <returns>IEnumerable of initialized objects.</returns>
    public static IEnumerable<T> GetAndActivateAllAssignableTo<T>()
    {
        return GetAndActivateAllAssignableTo<T>(null);
    }

    /// <summary>
    /// Fetches and prepares for initialization with a given constructor parameters all types in the current application domain which are assignable to T.
    /// </summary>
    /// <typeparam name="T">The type to which all desired types should be assignable to.</typeparam>
    /// <param name="consParams">The constructore parametes array - could be null</param>
    /// <returns>IEnumerable of initialized objects.</returns>
    public static IEnumerable<T> GetAndActivateAllAssignableTo<T>(object[] consParams)
    {
        //Deal with null reference for better code consistency
        if (consParams == null)
            consParams = new object[0];

        return from type in AppDomain.CurrentDomain.GetAllAssignableTo<T>()
               where type.IsInstantiable()
               select (T)Activator.CreateInstance(type, consParams);
    }

    /// <summary>
    /// Gets the flag which shows whether an object of a given type could be possibly(not guaranteed) instantiated.
    /// </summary>
    /// <param name="type">The type to check.</param>
    /// <returns>The flag which shows whether an object of a given type could be possibly(not guaranteed) instantiated.</returns>
    public static Boolean IsInstantiable(this Type type)
    {
        if (type == null)
            throw new ArgumentNullException("type", "The type is null");
        if (type.IsAbstract)
            return false;
        if (type.IsGenericTypeDefinition)
            return false;
        if (type.IsInterface)
            return false;

        return true;
    }

    /// <summary>
    /// Gets all types which are assignable to T type variables.
    /// </summary>
    /// <typeparam name="T">The type to which desired types should be assignable.</typeparam>
    /// <param name="appDomain">The app domain which assemblies should be checked</param>
    /// <returns>The IEnumerable of all types which are assignable to T type variables.</returns>
    public static IEnumerable<Type> GetAllAssignableTo<T>(this AppDomain appDomain)
    {
        if (appDomain == null)
            throw new ArgumentNullException("appDomain", "The app domain is null");
        return GetAllAssignableTo(appDomain, typeof(T));
    }


    /// <summary>
    /// Gets all types which are assignable to T type variables.
    /// </summary>
    /// <param name="appDomain">The app domain which assemblies should be checked</param>
    /// <param name="assignToType">The type to which desired types should be assignable.</param>
    /// <returns>The IEnumerable of all types which are assignable to T type variables.</returns>
    public static IEnumerable<Type> GetAllAssignableTo(this AppDomain appDomain, Type assignToType)
    {
        if (appDomain == null)
            throw new ArgumentNullException("appDomain", "The app domain is null");
        if (assignToType == null)
            throw new ArgumentNullException("assignToType", "The type to check is null");

        return from asm in appDomain.GetAssemblies()
               from type in asm.GetExportedTypes()
               where assignToType.IsAssignableFrom(type)
               select type;
    }
}

ИЗМЕНИТЬ:

@ Tracey23 Да, это не совсем то, что вы хотите. Он создает экземпляры всех классов, которые можно присвоить TClass (!! в этом коде TClass может быть даже интерфейсом!!), и любой TClass, конечно же, можно присвоить TClass.

Чтобы это решение работало так, как вы хотите, вы можете либо сделать свой базовый класс abstract, поскольку сомнительно, что вы используете его в каком-либо другом качестве, кроме как базовый класс для компонентов, либо изменить GetAllAssignableTo на использование (!assignToType.Equals(type)) в условии:

    public static IEnumerable<Type> GetAllAssignableTo(this AppDomain appDomain, Type assignToType)
    {
        if (appDomain == null)
            throw new ArgumentNullException("appDomain", "The app domain is null");
        if (assignToType == null)
            throw new ArgumentNullException("assignToType", "The type to check is null");

        return from asm in appDomain.GetAssemblies()
               from type in asm.GetExportedTypes()
               where assignToType.IsAssignableFrom(type) && (!assignToType.Equals(type)) // !!!!!!
               select type;
    }

Но это изменит семантику, поэтому вы можете переименовать эти методы, чтобы отразить, что они больше не будут включать базовый тип в результаты.

person Eugene Podskal    schedule 20.08.2014
comment
Большое спасибо, Евгений! К сожалению, ваш код содержит ошибку public static Boolean IsInstantiable(this Type type) this. Метод расширения должен быть определен в неуниверсальном статическом классе. - person Tracey23; 20.08.2014
comment
Поместите все эти методы в отдельный public static class и все должно работать (я имею в виду все из второго фрагмента кода). - person Eugene Podskal; 20.08.2014
comment
Я помещаю public static class в то же пространство имен: ` пространство имен PutObjectsInList { class Program ... public static class ReflectorHelper ... public class Class01 { }... }`, но я все еще получаю сообщение об ошибке. Имя «GetAndActivateAllAssignableTo» не существует. в текущем контексте - person Tracey23; 20.08.2014
comment
@ Tracey23 Tracey23 Да, я забыл добавить ReflectorHelper. в первый фрагмент, извините. - person Eugene Podskal; 20.08.2014
comment
большое спасибо! Он скомпилировался, но по какой-то причине поместил [0]{PutObjectsInList.Class01} +[1]{PutObjectsInList.SubClass0101} +[2]{PutObjectsInList.SubClass0102} +[3]{PutObjectsInList.SubClass0103} в listWithObjects. Тем не менее, я буду работать с ним! Спасибо еще раз!!! - person Tracey23; 20.08.2014

Начните с исправления возвращаемого типа.

SubClasses<TClass>() возвращает IEnumerable<Type>, поскольку возвращает последовательность объектов типа. PopulateListWithObjects<TClass>() будет возвращать список, содержащий экземпляры данного типа, поэтому аргумент типа для List<> должен быть наиболее производным базовым классом — я полагаю, это будет TClass. Другими словами, я думаю, вы имеете в виду

private static IEnumerable<TClass> PopulateListWithObjects<TClass>()
{
    var listWithObjects = new List<TClass>();

    // todo: add objects here

    return listWithObjects; 
}

Теперь, как вы собираетесь фактически создавать экземпляры для включения в список, это другой вопрос, и вы не предоставили достаточно информации, чтобы ответить на него.

person phoog    schedule 20.08.2014
comment
Спасибо! Это абсолютно правильно! Я обновил описание проблемы. - person Tracey23; 20.08.2014