我试图获得实现接口的类列表,然后在程序的稍后某个点,实例化这些类并将参数传递给它们的构造函数。
在前面的Stack Overflow页面中,我看到了用空构造函数实例化类的代码:
var preprocessors = from t
in Assembly.GetExecutingAssembly().GetTypes()
where t.GetInterfaces()
.Contains(typeof(Preprocessing))
&& t.GetConstructor(Type.EmptyTypes) != null
select Activator.CreateInstance(t) as Preprocessing;
但是我不希望某些类在没有向构造函数传递某种参数的情况下被实例化(参数是在for循环中获得的,所以我必须等到实例化它)。
我试着这样做,以获得要实例化的类列表:
var preprocessors = from t
in Assembly.GetExecutingAssembly().GetTypes()
select t.GetInterfaces()
.Contains(typeof(Preprocessing))
但是这样做之后,我不确定如何访问类并实例化它们。希望你能给点指导。谢谢! !
编辑:我不知道Activator.CreateInstance(...)
括号里应该写什么。我试着这样写:
foreach (var sim in similarities)
{
var a = Activator.CreateInstance(sim, preprocessedData) as Preprocessing;
但是这会抛出一个错误,很可能是因为preprocessedData
是一个DenseMatrix
对象(来自MathNet Numerics库)。有没有办法发送一个DenseMatrix
作为参数,而不是一个数组?
重载CreateInstance与params在这里是合适的
public static Object CreateInstance(
Type type,
params Object[] args
)
http://msdn.microsoft.com/en-US/library/wcxyzt4d (v = vs.110) . aspx
使用示例var constructorParams = new object[] { 1, "string", new object() }; //here is example of params that you will pass to each plugin constructor
var pluginTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t => typeof(Preprocessing).IsAssignableFrom(t)); //all types of your plugin
var plugins = pluginTypes.Select(pluginType => Activator.CreateInstance(pluginType, constructorParams)); //instanciated plugins
更新var a = Activator。CreateInstance(sim, new object[] {preprocessedData})
让我们想象sim有这样的实现:
class sim
{
public sim(int a, string b, AnotherType c){}
}
所以要用参数构造函数初始化这个类你必须传递三个参数,像
var a = Activator.CreateInstance(sim, new object[] { 1230, "string", new AnotherType() })
结果CLR通过反射会产生你的强度
这应该会给你一个当前应用域中实现指定接口的类型列表:
IEnumerable<Type> TypesImplementingInterface( Type interfaceType , params Type[] desiredConstructorSignature )
{
if ( interfaceType == null ) throw new ArgumentNullException( "interfaceType" ) ;
if ( !interfaceType.IsInterface ) throw new ArgumentOutOfRangeException( "interfaceType" ) ;
return AppDomain
.CurrentDomain
.GetAssemblies()
.SelectMany( a => a.GetTypes() )
.Where( t => t.IsAssignableFrom( interfaceType ) )
.Where( t => !t.IsInterface )
.Where( t => t.GetConstructor( desiredConstructorSignature ) != null )
;
}
一旦你有了这个,实例化的实例的类型是很容易的,沿着这样的行:
T ConstructInstance<T>( Type t , params object[] parameterList )
{
Type[] signature = parameterList
.Select( p => p.GetType() )
.ToArray()
;
ConstructorInfo constructor = t.GetConstructor( signature ) ;
T instance = constructor.Invoke( parameterList ) ;
return instance ;
}
在您的情况下,您需要这样做:
Type[] types = TypesImplementingInterface( typeof(IFoo) , typeof(DenseMatrix) ).ToArray() ;
DenseMatrix dmInstance = ... ;
...
IFoo constructedInstance = ConstructInstance<IFoo>( types[0] , dmInstance ) ;
CreateInstance有一个接受构造函数参数的签名:
活化剂。CreateInstance Method (Type, Object[])
编辑
这是一个例子:
interface ITest
{
string Say();
}
class Test1 : ITest
{
private string message;
public Test1(string message)
{
this.message = message;
}
public string Say()
{
return this.message;
}
}
class Test2 : ITest
{
private string message;
public Test2(string message)
{
this.message = message;
}
public string Say()
{
return this.message;
}
}
void Main()
{
string[] args = new string[] { "Hello", "World" };
var tests = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.GetInterfaces().Contains(typeof(ITest)))
.Select((t, i) => Activator.CreateInstance(t, args[i]) as ITest);
foreach (var item in tests)
{
Console.WriteLine(item.Say());
}
}
其他人提出的反射机制可能会失败,如果你有一大批程序集要查看- . net VM延迟加载,程序集在实际引用之前不会加载,也就是说,从该程序集显式引用一个类。
如果你的程序引用了20个程序集,而实际上只加载了其中的2个,AppDomain.GetAssemblies()将不会返回其他18个,你将永远不会在它们中查找接口的实现。
有多种方法可以强制加载程序集。您可以查看调用堆栈,如下所示,或者您可以查看当前程序集并强制加载它们的引用程序集,等等。
下面是一个尝试处理这些问题的完整类:
public class PluginMgr<T> where T : class
{
public PluginMgr()
{
this.Plugins = new List<T>();
}
/// <summary>
/// The list of plugin instances that were found and created.
/// </summary>
public List<T> Plugins { get; private set; }
/// <summary>
/// Scans loaded assemblies for classes that implement the interface specified by the type
/// parameter, then instantiates and stores any classes that are found.
/// </summary>
public void Initialize()
{
ForceLoadAssemblies();
FindPlugins();
}
/// <summary>
/// Attempts to force the VM to load all assemblies that are referenced by any assembly
/// implicated in the call stack. Referenced assemblies are not loaded until reference
/// resolution happens that depends on that assembly; if an assembly X has a reference to
/// another assembly Y, but X never uses anything from Y, then Y is never loaded; that is to
/// say, the .Net assembly loader uses 'lazy loading'.
///
/// This is necessary because our plugin sniffing logic won't find the plugins that don't
/// have their defining assembly loaded. If we forcibly load the assembly, then we'll guarentee
/// that we find the assembly.
/// </summary>
private void ForceLoadAssemblies()
{
StackFrame[] frames;
AssemblyName[] refedAssemblies;
Assembly assembly;
frames = new StackTrace().GetFrames();
foreach (StackFrame frame in frames)
{
assembly = frame.GetMethod().DeclaringType.Assembly;
refedAssemblies = assembly.GetReferencedAssemblies();
foreach (AssemblyName refedAssembly in refedAssemblies)
{
Assembly.Load(refedAssembly);
}
}
}
/// <summary>
/// Scan through every loaded assembly to find types that are implementors of our
/// given interface.
/// </summary>
private void FindPlugins()
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
Type interfaceType = typeof(T);
foreach( Assembly assembly in assemblies )
{
Type[] types = assembly.GetExportedTypes();
foreach( Type type in types )
{
if( type.GetInterfaces().Contains( interfaceType ) )
{
T plugin = Activator.CreateInstance( type ) as T;
this.Plugins.Add( plugin );
}
}
}
}
感谢大家的回复,他们帮了我很大的忙。
这里的一些其他解决方案可能也等同于我所做的,但这个最终对我有效。
var preprocessors = from t in Assembly.GetExecutingAssembly().GetTypes()
where t.GetInterfaces().Contains(typeof(Preprocessing))
select Activator.CreateInstance(t, originalData) as Preprocessing; // Create instance of class with originalData as parameter
// Obtain a list of all types in the assembly (will be instantiated in the foreach loop)
var similarities = Assembly.GetExecutingAssembly().GetTypes();
这是我如何创建一个实例,并在for循环中传递参数:
foreach (var sim in similarities)
{
if (sim.GetInterfaces().Contains(typeof(Similarity))) // Check if the assembly types are of type Similarity
{
// Create instance of similarity class with preprocessedData as a parameter
var similarityObject = Activator.CreateInstance(sim, preprocessedData) as Similarity;