如何获取实现接口的类列表?

时间:2014-07-03 19:43:45

标签: c# reflection interface

我正在尝试获取实现接口的类列表,然后在程序的稍后部分实例化这些类并将参数传递给它们的构造函数。

在之前的Stack Overflow page中,我看到这段代码使用空构造函数实例化了类:

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;

但是这会引发错误,很可能是因为preprocessedDataDenseMatrix对象(来自MathNet Numerics库)。有没有办法发送DenseMatrix作为参数而不是数组?

5 个答案:

答案 0 :(得分:3)

带有params的CreateInstance的重载适用于此处

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通过反射将产生你的内容。

答案 1 :(得分:1)

这应该为您提供当前应用程序域中实现指定接口的类型列表:

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

答案 2 :(得分:0)

CreateInstance有一个签名,它带有构造函数参数:

Activator.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());
    }
}

答案 3 :(得分:0)

如果您有大量的程序集要查看,其他人提供的反射机制可能会失败 - .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 );
                }
            }
        }
    }

答案 4 :(得分:0)

感谢所有回复,他们帮助我解决了这个问题。

这里的其他一些解决方案也可能与我所做的相同,但最终为我工作。

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-loop中传递参数的方法:

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;