Would you be so kind to help me composing the method which would create a list of objects.
Clarification: I would like to have a list similar to
List<Class01> list = new List<Class01>();
list.Add(new SubClass0101());
list.Add(new SubClass0102());
list.Add(new SubClass0103());
but it has to grow automatically, should the amount of subclasses increase.
Question: I need help with private static IEnumerable<Type> PopulateListWithObjects<TClass>()
Please see code below for more details.
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 {}
}
Your private static IEnumerable<Type> PopulateListWithObjects<TClass>
should have return type of IEnumerable<TClass>
if you want to achieve the desired result.
Because Type
denotes not the object of some type, but their type - metadata identifier System.Type
.
For creation(instantiation) of your instance you can use Activator.CreateInstance() method.
In the end it will look like:
private static IEnumerable<TClass> PopulateListWithObjects<TClass>()
{
return ReflectorHelper.GetAndActivateAllAssignableTo<TClass>();
}
With GetAndActivateAllAssignableTo defined as:
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;
}
}
EDIT:
@Tracey23 Yes, it does not exactly do what you want. It instantiates all classes that are assignable to TClass
(!!in this code TClass can be even an interface!!), and any TClass
is ofcourse assignable to TClass
.
For this solution to work as you want, you may either make your Base class abstract
, as it is doubtful that you use it in any other capacity than as base class for the components or change GetAllAssignableTo
to use (!assignToType.Equals(type))
in condition:
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;
}
But it will change the semantics, so you may want to rename this methods to reflect that they will no longer include Base type in results.