Click here to Skip to main content
15,867,568 members
Please Sign up or sign in to vote.
4.00/5 (3 votes)
See more: , +
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:
C#
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;
}
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.
C#
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;
}
The problem is that the Assembly in my second solution is found, but it can't get the Type information.
If I change
C#
if (t.GetInterfaces().Contains(typeof(IHeaderBarComponent)))

to
C#
if (t.GetInterfaces().Any(it => it.FullName == typeof(IHeaderBarComponent).FullName))
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?

Edit:
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.
Posted
Updated 13-Jan-13 22:16pm
v2
Comments
Sergey Alexandrovich Kryukov 12-Jan-13 20:32pm    
Hi Naerling,

Good to hear from you.

Overall, this is really interesting and important question, but it suffers from not explaining your ultimate goal well enough, and probably due to that, the problem itself is also not 100% clear.

I also voted 4 for the question, as someone did before me.

However, as I'm well familiar with a set of related problems, I offered my explanation of couple of delicate moment which should help you to resolve your problem, but if there is something else, you are very welcome to ask further questions, as always.

—SA
Sander Rossel 14-Jan-13 4:23am    
Thanks Sergey, I have updated my question to make my ultimate goal more clear. If you have any questions I will try to explain it further.
Once again your help is greatly appreciated.

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

Is your purpose selecting the type which implements IHeaderBarComponent?
This is how it's done:
C#
System.Type headerBarComponentType = typeof(IHeaderBarComponent);

//...

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


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
 
Share this answer
 
v2
Comments
Sander Rossel 14-Jan-13 4:22am    
Hi Sergey,
Thanks for your answer. I didn't know the IsAssignableFrom method. Exactly what I needed. However, this still does not fix my problem. All my methods, and also your recommendations, seem to work perfectly well at run-time, but my code executes at design-time.
Also, the Assembly which checks for types of IHeaderBarComponents is referenced by an Assembly from which the IHeaderBarComponents come from. Somehow it does not recognize the IHeaderBarComponent Type from the other Assembly.
I have updated my question, hopefully this will make the problem, and my goal clearer.
Sergey Alexandrovich Kryukov 14-Jan-13 17:31pm    
Thank you for some clarification, but now... If you still have a problem, you probably need to create a sample prototype to reproduce the problem, because I cannot see right now, what else could go wrong. Of course, if the problem appears only at design-time, only under the Visual Studio, it's a considerable hassle. Let me ask you this explicitly: you cannot reproduce the problem on a stand-along application process, is that true?

One approach is to use two Visual Studio processes, one as the debugger, another one to reproduce the problem. Basically, you can execute one Visual Studio process under the debugger of another one. Chances are, you would be able to put a break point to some place in your code, before you use "Attach process..."

However, I could probably see the problem before such a hard debugging, but I would need to see the complete solution, more exactly, its highly simplified prototype. I can look at how you define the interface, implement it in one assembly, load that assembly and try to find the class, instantiate it, use the interface.

Is it just an advanced control, or a whole UI development system you are trying to create?

—SA
Sander Rossel 18-Jan-13 6:28am    
Hi Sergey,
I'm sorry for my belated reply. I've been busy, but I've also found out why it is not possible what I want. See my own answer for details.
If you have any questions or remarks you're welcome to ask. If you still want a prototype for personal interest let me know and I'll create one for you which explains the problem.
Thanks for your help. It is greatly appreciated.
Sergey Alexandrovich Kryukov 18-Jan-13 12:51pm    
Still not accepting my answer formally? It's all true, taking into account what was asked.

I don't think looking at the prototype which does not do what you meant to do would be very fruitful; I'm busy, too. It would be more useful if you could formulate your goals in even more general aspect. The successful architectures based on Reflection I created no one and not two...

—SA
Sander Rossel 18-Jan-13 15:35pm    
While I appreciate your help and good advice you never mentioned anything about design time Reflection and Assemblies, though I did mention design time in my question. So I gave you a 5 for the information, but it did not actually solve my problem.

Are my goals still not clear? Unfortunately I can't make them any clearer in words, so my apologies for that. Formulating a correct question can sometimes be as hard as finding the correct answer to that question :)
I'll give it another shot.
I have two assemblies, A and B. B references A. Assembly A has a Control which has a list of IHeaderBarComponents. When you click on the 'edit' icon in the property window (WinForms) you get the default collection editor. Now what I want is that the user is able to select ALL types that inherit from IHeaderBarComponent in both assemblies A and B. That would require me to loop through all types in Assembly B, which is the project I am currently working on. Unfortunately this Assembly is not loaded in the AppDomain and as such I cannot get information on the types in that Assembly. Loading Assembly B manually would result in not being able to compile it anymore because it's running.
So that's why it's not possible to iterate over every type in Assembly B from Assembly A.

well, that was a final try at describing my problem. I'll try to keep future problems a bit simpler to explain and answer... ;)
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.
 
Share this answer
 
Comments
Sergey Alexandrovich Kryukov 22-Jan-13 10:30am    
If you are "actually working on this Assembly", it is still loaded in certain sense, don't you think so? Maybe it's loaded is "reflection-only"… could it be possible?
—SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900