使用反射动态实例化 类 扩展基类
Dynamically instantiate classes extending baseclass using reflection
很长一段时间以来,我一直在努力寻找一种方法来动态实例化所有扩展特定基础class的classes(在运行时)。根据我的阅读,它应该使用 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 class 包含一个通用方法,它应该 return 一个 IEnumerable<T>
包含实例化的 classes扩展 Vehicle
,意思是 Car, Bike & Scooter
.
下面发布的代码是 InstanceService class 的当前状态,在尝试了大量不同的解决方案之后,这意味着它主要包含调试工具。
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
class 的代码及其实现之一。
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;
}
}
}
这适用于任何可以使用默认构造函数实例化的类型。你的 classes 是从另一个 class 派生出来的这一事实是无关紧要的,除非我遗漏了什么......
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;
}
}
我想你应该感兴趣的方法是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)
);
很长一段时间以来,我一直在努力寻找一种方法来动态实例化所有扩展特定基础class的classes(在运行时)。根据我的阅读,它应该使用 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 class 包含一个通用方法,它应该 return 一个 IEnumerable<T>
包含实例化的 classes扩展 Vehicle
,意思是 Car, Bike & Scooter
.
下面发布的代码是 InstanceService class 的当前状态,在尝试了大量不同的解决方案之后,这意味着它主要包含调试工具。
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
class 的代码及其实现之一。
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;
}
}
}
这适用于任何可以使用默认构造函数实例化的类型。你的 classes 是从另一个 class 派生出来的这一事实是无关紧要的,除非我遗漏了什么......
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;
}
}
我想你应该感兴趣的方法是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)
);