Динамически создавать экземпляры классов, расширяющих базовый класс, используя отражение - PullRequest
0 голосов
/ 04 сентября 2018

В течение долгого времени я пытался найти способ динамически создавать экземпляры всех классов, которые расширяют определенный базовый класс (во время выполнения). Из того, что я прочитал, это должно быть сделано с помощью Reflection, к сожалению, я еще не понял, как это сделать.

Структура моего проекта выглядит следующим образом:

Library
--|
  |
  --Vehicle.cs (abstract class)
  |
  --Car.cs (extending vehicle)
  |
  --Bike.cs (extending vehicle)
  |
  --Scooter.cs (extending vehicle)
  |
  --InstanceService.cs (static class)
  |
  |
ConsoleApplication
--|
  |
  --Program.cs

Класс InstanceService содержит универсальный метод, который должен возвращать IEnumerable<T>, содержащий экземпляры классов, расширяющие Vehicle, что означает Car, Bike & Scooter.

Код, приведенный ниже, является текущим состоянием класса InstanceService , после того, как испробовал множество различных решений, то есть он в основном содержит инструменты для отладки.

InstanceService.cs

using System;
using System.Collections.Generic;

namespace Library
{
    public static class InstanceService<T>
    {
        //returns instances of all classes of type T
        public static IEnumerable<T> GetInstances()
        {

            var interfaceType = typeof(T);
            List<T> list = new List<T>();
            Console.WriteLine("Interface type: " + interfaceType.ToString());
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach(var assembly in assemblies)
            {
                Console.WriteLine("Assembly: " + assembly.ToString());
                if (assembly.GetType().IsAbstract)
                {
                    var instance = (T) Activator.CreateInstance(assembly.GetType());
                    list.Add(instance);
                }
            }
            return list;
        }
    }
}

Я также приложил код для абстрактного класса Vehicle и одну из его реализаций.

Vehicle.cs

namespace Library
{
    public abstract class Vehicle
    {
        protected float maxSpeedInKmPerHour;
        protected float weightInKg;
        protected float priceInDkk;
    }
}

Car.cs

namespace Library
{
    public class Car : Vehicle
    {

        public Car()
        {
            this.maxSpeedInKmPerHour = 1200;
            this.weightInKg = 45000;
            this.priceInDkk = 71000000;
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 04 сентября 2018

Я думаю, что метод, который должен вас заинтересовать: IsAssignableFrom.

Кроме того, с LINQ код намного проще, если вам разрешено его использовать, и поскольку вы создаете объекты по одному, я предлагаю использовать yield return.

static IEnumerable<T> GetInstances<T>() 
{
    var baseType = typeof(T);
    var types = AppDomain.CurrentDomain.GetAssemblies()
        .SelectMany( a => a.GetTypes() )
        .Where
        (
            t => baseType.IsAssignableFrom(t)                  //Derives from base
              && !t.IsAbstract                                 //Is not abstract
              && (t.GetConstructor(Type.EmptyTypes) != null)   //Has default constructor
        );


    foreach (var t in types)
    {
        yield return (T)Activator.CreateInstance(t);
    }
}

Или, если по какой-то причине вы хвастаетесь и хотите сделать это одним утверждением:

    var types = AppDomain.CurrentDomain.GetAssemblies()
        .SelectMany( a => a.GetTypes() )
        .Where
        (
            t => typeof(T)IsAssignableFrom(t)  
              && !t.IsAbstract 
              && (t.GetConstructor(Type.EmptyTypes) != null) 
        )
        .Select
        (
            t => (T)Activator.CreateInstance(t)
        );
0 голосов
/ 04 сентября 2018

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

private T MakeInstance<T>()
{
    // the empty Type[] means you are passing nothing to the constructor - which gives
    // you the default constructor.  If you need to pass in an int to instantiate it, you
    // could add int to the Type[]...
    ConstructorInfo defaultCtor = typeof(T).GetConstructor(new Type[] { });

    // if there is no default constructor, then it will be null, so you must check
    if (defaultCtor == null)
        throw new Exception("No default constructor");
    else
    {
        T instance = (T)defaultCtor.Invoke(new object[] { });   // again, nothing to pass in.  If the constructor needs anything, include it here.
        return instance;
    }
}
...