【问题标题】:Dynamically instantiate classes extending baseclass using reflection使用反射动态实例化扩展基类的类
【发布时间】:2018-09-04 16:48:18
【问题描述】:

长期以来,我一直在努力寻找一种方法来动态实例化所有扩展特定基类的类(在运行时)。根据我的阅读,它应该使用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;
        }
    }
}

【问题讨论】:

  • 目前的方法(具体而言)出了什么问题?
  • 没什么特别的,至少它没有返回任何错误。我只是好奇如何以最好的方式实现这一点。此外,使用当前方法,该函数似乎只返回一个无法循环的对象,而实际上它应该返回一个包含 3 个从Vehicle 派生的实例化类的列表。然后我在函数返回的列表上使用ToString(),得到如下输出System.Collections.Generic.List1[Library.Vehicle]

标签: c# .net reflection system.reflection decoupling


【解决方案1】:

我认为你应该感兴趣的方法是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)
        );

【讨论】:

  • 你是冠军!这就像一个魅力,很容易理解。非常感谢!
【解决方案2】:

这应该适用于可以使用默认构造函数实例化的任何类型。你的类是从另一个类派生的这一事实是无关紧要的,除非我遗漏了什么......

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;
    }
}

【讨论】:

  • 但是使用这种方法,您似乎只创建了一个实例?我想当我创建从基类派生的所有类的实例时,方法有点不同?
  • 是的,这会创建一个实例。如果你想要一个特定类型的多个实例,你可以在一个循环中运行它,并在实例化它们时将它们添加到一个集合中。我不太确定你在这里要做什么 - 如果你想要一个新的,那么 John Wu 的方法会更好,但除非你不断添加类,否则我只会手动实例化每个类并避免使用像 InstanceService 这样的反射和泛型(它们都是为您在编译时不知道所需数据类型的情况而设计的 - 听起来您事先就知道了)
  • 是的,没错。但是,这与我想要的无关,因为这是一项任务,而不是实际的应用程序。并且在分配中指定了以下内容:使用方法创建一个 InstanceService IEnumerable&lt;T&gt; GetInstances() 返回所有类型为 T 的类的实例
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-04
  • 2021-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多