来自非引用程序集的类型问题 [英] Problem with types from non-referenced assemblies

查看:82
本文介绍了来自非引用程序集的类型问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的项目有接口 ISomething ,已定义。

现在,在我的项目的其他地方,我希望获得实现 ISomething 的所有类型,以及调用特定方法的 Assemblies 。这给了我一些问题。

所以我有:

I have a project which has an Interface, ISomething, defined.
Now elsewhere in my project I want to get all types which implement ISomething, also from Assemblies that call the specific method. This is giving me quite some problems.
So I have:

public interface ISomething {}

// In some class...
public IEnumerable<Type> GetTypes()
{
    List<Type> types = new List<Type>();
    foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
    {
        foreach (Type t in asm.GetTypes())
        {
            if (t.GetInterfaces().Contains(typeof(IHeaderBarComponent)))
            {
                types.Add(t);
            }
        }
    }
    return types;
}

然后在另一个解决方案中我引用这个dll,创建一个 Class SomeClass ,实现 ISomething 并调用方法。

现在我希望 GetTypes 函数返回 SomeClass 。在运行时这没有问题,但是这里是捕获...该方法在设计时被调用(更具体地,对于 CollectionEditor )。

我试图首先加载程序集(这不是必需的,因为程序集已经加载了),但是没有用。

Then in another solution I reference this dll, create a Class, SomeClass, that implements ISomething and call the method.
Now I expect the GetTypes function to return SomeClass. At runtime this is no problem, but here''s the catch... The method is called at design time (more specific, for a CollectionEditor).
I tried to load the Assemblies first (which shouldn''t be necessary, since the Assembly is already loaded), but that didn''t work.

public IEnumerable<Type> GetTypes()
{
    List<Type> types = new List<Type>();
    foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
    {
        Assembly loadedAsm = Assembly.Load(asm.GetName());
        foreach (Type t in loadedAsm.GetTypes())
        {
            if (t.GetInterfaces().Contains(typeof(IHeaderBarComponent)))
            {
                types.Add(t);
            }
        }
    }
    return types;
}

问题是找到了我的第二个解决方案中的 Assembly ,但它不能得到键入信息。

如果我更改

The problem is that the Assembly in my second solution is found, but it can''t get the Type information.
If I change

if (t.GetInterfaces().Contains(typeof(IHeaderBarComponent)))






to

if (t.GetInterfaces().Any(it => it.FullName == typeof(IHeaderBarComponent).FullName))

它实际上起作用我得<$我的结果中有c $ c> SomeClass 。但是,当在类型上调用类似 Activator.CreateInstance 的函数时,我收到类型不是的错误消息输入ISomething或装配不能等等。



任何想法都是?



编辑:

由于我的最终目标还不完全清楚,我会在这里尝试描述它。

我有一个 Control 其中 IHeaderBarComponent 的集合为 Property 。我还有一个 System.ComponentModel.Design.CollectionEditor [ ^ ]在设计时添加和删除此集合中的项目。 Control 的用户应该能够创建自己的 IHeaderBarComponent CollectionEditor 然后应该显示这种类型的 IHeaderBarComponent 。所以 CreateNewItemTypes [< a href =http://msdn.microsoft.com/en-us/library/system.componentmodel.design.collectioneditor.createnewitemtypes(v=vs.100).aspx\"target =_ blanktitle =New Window > ^ ] CollectionEditor 的方法应返回所有已加载程序集中实现 IHeaderBarComponent 的所有类型(注意)包含 IHeaderBarComponent 程序集未引用程序集自定义 IHeaderBarComponents ,而不是带有自定义 IHeaderBarComponents 汇编引用汇编,其中上面的代码执行)。

我使用的方法如上所述,在运行时运行良好。但是,在设计时调用 CreateNewItemTypes ,结果是它无法从类型在加载的(未引用的)程序集中。 IsAssignableFrom 等方法始终返回false。比较名称似乎有效,虽然新实例永远无法实例化。

我希望这可以解决问题。

it actually works in that I get SomeClass in my result. However, when calling a function like Activator.CreateInstance on the Type I get the error message that the type is not of type ISomething or that the assembly could not be loaded etc.

Any idea''s?


As my ultimate goal is not completely clear I will try and describe it here.
I have a Control which has a collection of IHeaderBarComponent as Property. I also have a System.ComponentModel.Design.CollectionEditor[^] to add and remove items from this collection at design time. The user of the Control should be able to create his own IHeaderBarComponent and the CollectionEditor should then display this type of IHeaderBarComponent. So the CreateNewItemTypes[^] method of the CollectionEditor should return all types that implement IHeaderBarComponent from all loaded assemblies (note that the Assembly which contains IHeaderBarComponent does not reference Assemblies with custom IHeaderBarComponents, rather the Assembly with custom IHeaderBarComponents references the Assembly in which the code above executes).
The method I use is described above and works great at runtime. However, the CreateNewItemTypes is called at design-time and the result is that it somehow can''t load information from the Types in the loaded (not referenced) Assemblies. Methods such as IsAssignableFrom always return false. Comparing names seems to work, although new instances can never be instantiated.
I hope this clears up the problem.

推荐答案

第一个错误点在我的代码中我可以看到:

First wrong point in the I can see in your code is this:
if (t.GetInterfaces().Contains(typeof(IHeaderBarComponent))) ...



您的目的是选择实现<$ c的类型$ c> IHeaderBarComponent


这就是它的完成方式:


Is your purpose selecting the type which implements IHeaderBarComponent?
This is how it''s done:

System.Type headerBarComponentType = typeof(IHeaderBarComponent);

//...

//now, in your loop:
if (headerBarComponentType.IsAssignableFrom(t)) { /* add this type */ }





如果某些类型 t 实现 IHeaderBarComponent ,具有此接口的编译时类型的某个对象可以从 t 的实例分配,然后才能分配。显然,不是吗?



另外,你应该避免得到任何名字。这绝对不是必需的。实际上,定义接口可能是识别和过滤通过Reflection实例化的类/结构的最佳方法。接口类型本身就是标识对象,运行时系统(CLR)负责其唯一性。



现在,您对加载程序集的考虑不明确或不正确。如果正式引用程序集,那么它们可能会在您执行Reflection代码时加载,但它们不是,您需要先加载它们。但是你在问题的标题中提到未引用的集合。你需要加载它们。



现在,这是一个重要的事实,在Microsoft文档中没有明确解释,或者,可能根本没有解释。您可以通过两种不同的方式获得装配集合。首先是你的 AppDomain.CurrentDomain.GetAssemblies()。第二种方法是使用 System.Reflection.Assembly.GetReferencedAssemblies http://msdn.microsoft.com/en-us/library/system.reflection.assembly.getreferencedassemblies.aspx [ ^ ]。



使用第二种方法,您可以进行一些程序集(例如,您的入口程序集,但它可以是任何东西),获取其引用程序集并继续递归收集程序集。通过这种方式,您可以收集给定程序集所依赖的所有程序集(或者可能依赖于它们)。



现在,如果比较结果(不考虑在加载期间加载的程序集)运行时间,这是你可以随时考虑的明显因素;为了简化解释,让我们说你不加载任何东西,你会看到,一般情况下结果是不同的。程序集的枚举 System.AppDomain.GetAssemblies 优化正式引用但未实际使用的程序集。另一种方法列举了它们所有



你应该根据你真正需要的方法选择一种或另一种方法。



-SA



If some type t implements IHeaderBarComponent, some object with the compile-time type of this interface can be assignable from an instance of t, then and only then. Obviously, isn''t it?

Also, you should avoid getting anything by name. This is absolutely not needed. Actually, defining an interface is probably the best method of identification and filtering of the classes/structures instantiated through Reflection. The interface type is the identity object by itself, and run-time system (CLR) takes care of its uniqueness.

Now, your consideration about loading assemblies in not clear or incorrect. If assemblies are formally referenced, they might be loaded by the time you execute your Reflection code, but it they are not, you need to load them first. But you mention "unreferenced assemblies" in the title of the question. You need to load them.

Now, this is one important fact that is not clearly explained in Microsoft documentation, or, probably, not explained at all. You can get a collection of assemblies in two different ways. First is your AppDomain.CurrentDomain.GetAssemblies(). The second approach is using System.Reflection.Assembly.GetReferencedAssemblies, http://msdn.microsoft.com/en-us/library/system.reflection.assembly.getreferencedassemblies.aspx[^].

With second approach, you can take some assembly (for example, your entry assembly, but it can be anything), get its reference assemblies and continue collecting assemblies recursively. In this way, you can collect all assemblies some given assembly depends on (or potentially may depend).

Now, if you compare the results (not considering assemblies loaded during run time, which is the obvious factor you can always take into account; for simplicity of explanation, let''s say you don''t load any), you will see, that in general case the results are different. The enumeration of assemblies System.AppDomain.GetAssemblies "optimizes out" the assemblies which are formally referenced but not actually used. The other method enumerates them all.

You should choose one or another method depending on what you really need.

—SA


这个问题比我怀疑的要困难得多。特别是要清楚详细地解释这个问题。

幸运的是,我找到了自己的解决方案。这不可能是我想要的。



我想在大会期间(在Visual Studio中)工作时反映大会中的类型<你设计时间。这似乎是一个问题,因为为了反映程序集中的类型,必须在当前的AppDomain中加载程序集(AppDomain.Current.GetAssemblies()应该返回程序集)。但是,我正在研究的大会从未加载过。有充分的理由,所以我发现了。当程序集运行时,它无法更改,因此在卸载程序集之前无法重新编译程序集。当你真正在这个大会上工作时,这是不实际的,因为你不断改变和构建(从而重新编译)它。我猜这也是为什么Visual Studio不加载你目前正在处理的Assemblies的原因。


所以即使它有可能(我发现一些在设计时手动加载程序集的方法)我不希望这样。
This question turned out to be more difficult than I suspected. Especially to explain the problem clearly and in detail.
Luckily I sort of found my own solution. It''s just not possible what I want.

I wanted to reflect on types in the Assembly I''m currently working on (in Visual Studio) during design time. This seemed like a problem, because to reflect the types in an Assembly the Assembly must be loaded in the current AppDomain (AppDomain.Current.GetAssemblies() should return the Assembly). However, the Assembly I was working on was never loaded. For a good reason, so I found out. When an Assembly is running it cannot be changed, thus making re-compiling the Assembly impossible until the Assembly is unloaded. This is not practical when you''re actually working on this Assembly because you''re continuously changing and building (thus re-compiling) it. I guess that''s also the reason why Visual Studio does not load Assemblies you''re currently working on.

So even if it was possible (and I found some ways to manually load Assemblies at design time) I would not want this.


这篇关于来自非引用程序集的类型问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆