Click here to Skip to main content
Rate this: bad
good
Please Sign up or sign in to vote.
See more: .NET C#4.0 VB10.0 VB.NET .NET4 , +
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;
}
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;
}
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))
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 12-Jan-13 2:23am
Edited 13-Jan-13 22:16pm
v2
Comments
Sergey Alexandrovich Kryukov at 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
Naerling at 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.
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 1

First wrong point in the I can see in your code is this:
if (t.GetInterfaces().Contains(typeof(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 */ }
 
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
  Permalink  
v2
Comments
Naerling at 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 at 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
Naerling at 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 at 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
Naerling at 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... ;)
Sergey Alexandrovich Kryukov at 18-Jan-13 15:53pm
   
Not solving your problem is not my fault at all. But, whatever...
 
Of course you goal is not clear. Probably you don't understand what I mean. Your explanation is based on some architecture you keep in mind. Call it preoccupation. Now, you say, your problem is not solvable. But it means that you need to go to more general level. You need to explain what is the purpose of this control and this interface. What makes you thinking that this is what you really need?.
 
In particular, what makes you thinking that you should not load the application you consider.
 
By the way, if you really don't want to do that (suppose you have to "probe" the assembly first), there is a clear solution: you have to create a temporary (or just reloadable) AppDomain. If you take this route, there is no any other solution.
 
Here is why: unloading of assemblies is not allowed in .NET, by apparent security reasons. But as AppDomain data is fully isolated, it is allowed to create and unload the whole AppDomain. Actually, I widely use it, because in the scenario when you use assemblies on temporary basis, there is no other solution, and it should not be. So, you create AppDomain, load an assembly in it, use it, and, on certain evens, unload the whole domain. Of course, you use IPC to communicate between the domains. Fortunately, AppDomain have highly simplified IPC.
 
Will this solve your problem?
 
If not, you could possibly try to share with me you ultimate goal, more or less free from your idea on what exactly to do with the assemblies... :-)
 
—SA
Naerling at 21-Jan-13 3:48am
   
Ahhhhhh! NOW I understand what you mean. I am indeed stuck in a certain train of thought and perhaps I should just move out of it :)
 
Allright, so here it goes again.
I am creating a base Form for my compnay. Now I have a component called the HeaderBar which is basically just a FlowLayoutPanel. In this HeaderBar you must be able to put Controls that Inherit from HeaderBarComponentBase (which Inherits from Control). This Control, of course, does some stuff which is HeaderBarComponent specific such as regulate the size of the component.
Now the thing is I want programmers to be able to Inherit from this HeaderBarComponentBase after which their own HeaderBarComponent can be placed in the HeaderBar. HeaderBarComponents should not be placed on the Form, company policy to enforce certain layout rules.
So to accomplish this I am working with a custom CollectionEditor which loops through the types in an Assembly and checks for HeaderBarComponentBase types. If HeaderBarComponents are found you can add them using the CollectionEditor whose collection notifies the Form which places them in the HeaderBar.
The difficulty lies in that HeaderBarComponents cannot be dragged in the HeaderBar because the HeaderBar is part of the base Form and is not accessible to inheritors.
 
Sorry I couldn't explain this earlier. I was to focused on looping through types in an Assembly.
Perhaps I have indeed chosen a wrong approach and I need to let go of the idea to check for types in Assemblies.
Your idea's are greatly appreciated and more than welcome.
Sergey Alexandrovich Kryukov at 21-Jan-13 4:02am
   
:-)
Naerling at 21-Jan-13 6:27am
   
You're leaving a smiley, but no answer? That's not something I can smile about... You were my only hope for success!
Of course I know better than that. You will come with an answer that will blow my mind and all of my problems will be solved, right? :-)
Sergey Alexandrovich Kryukov at 21-Jan-13 11:00am
   
Thank you for your words, but I don't know yet. Sorry for confusing you with my previous reply, I just tried to "begin invoke", to leave myself your message in notification area, so I could look at it later.
—SA
Sergey Alexandrovich Kryukov at 21-Jan-13 11:12am
   
Speaking of the "check for types in Assemblies". Do you see that it's quite possible? What is the idea? It it a plug-in system, or something else? By the way, you still never answered about the goal of all of this. Is it a plug-in system, developing tool, or what? What should it do?
 
So, about checking types. Did you notice, from my past answered referenced, that I switched to alternative approach: before checking type, I have a layer for checking what to check. I call the assemblies to be loaded "plug-in assemblies", the one loading others "host assembly". Create a attribute, say ImplementationType. It's parameter should be Type, its target types should include only one: assembly. Plug-in assemblies apply this attribute to assembly. You could allow multiple attributes per implementing plug-in assembly. As the parameter is Type (not a string, like type name or name of anything), it will always be a really existing type. Alternative, it could be two Type parameters, one being an interface, another the implementation type. The attribute should claim that some interface in implemented by some type.
 
Now, it is impossible to guarantee, but still Reflection is greatly cut down. The host assembly lists all those attributes and picks up the interfaces it is interested in (for example, it it builds custom part of menu, it looks for interface designed for plug-in menu items). Then, it takes the implementing type from the attribute, it checks up that one type implements another (interface), IsAssignableFrom.
 
No need to traverse all types of the assembly. But it was in one of my past answers...
 
—SA
Naerling at 21-Jan-13 16:07pm
   
The problem is that I am creating a development tool. A base form for our programmers to make their life easier and to force them into a direction that ensures a common style among forms. That means the assemblies are loaded at design time. If an assembly is loaded you cannot simply replace or rebuild it, which means the assembly should be unloaded first. How would you do that since assemblies can't be unloaded unless they're in a seperate appdomain? And how to trigger the unloading? And if the appdomain is unloaded will the application still work since it now uses objects from assemblies that aren't loaded? And if I get all that right how do I tell my less skilled colleagues how to create and load plug-ins... I am actually re-writing this thing because it was too abstract for my colleagues to work with. They needed something simpler.
That's why I went with the idea to simply load the types from the assembly you're currently working on, but then I stumbled upon the problem that this assembly is not loaded, and for a good reason. From there it kind of escalated to the point where we are now :-)
 
I like your idea of using assembly attributes by the way. I will certainly be using that in the near future. Unfortunately I still don't see how it helps in this scenario.
Sergey Alexandrovich Kryukov at 21-Jan-13 16:35pm
   
I understand. A development tool was my guess; and I think this is the first time you mention it. I understand why design time creates the problem here.
 
I can tell you what I think about it, but it may look too unusual, I'm not sure. Here is the thing: I am a strong opponent of the designer approach in principle. I seriously think it develops in wrong direction. XAML is somewhat better than Forms, but still not a right direction. Designer is replacing of highly rational development with highly manual development, "clicks", where the "Don't Repeat Yourself" principle is heavily broken. Everything is repeated, manually.
 
Programming with Designer looks like going back to stone age by time when electric machines are already invented.
 
By these reasons, I never develop components which comply with design time. This is only needed if you want to cell components, but I hope to avoid this activity.
 
—SA
Naerling at 21-Jan-13 16:58pm
   
Well, that's good and all, but it doesn't solve my problem. And I can't tell my boss and colleagues, who have been using WinForms for years, "well guys, let's just don't do this anymore". I'll be keelhauled :-)
I also don't see how things are repeated? Controls have a huge inheritance hierarchy (which hints to some re-use) and other than that it's just a huge list of constructor calls and setting properties. Now something that could've been avoided by a for loop or something.
 
Anyway, by saying "I understand why design time creates the problem here" do you agree with me that my idea of looping through types in an assembly is simply not possible or desired? :-)
Sergey Alexandrovich Kryukov at 21-Jan-13 17:07pm
   
I did not pretend that by my previous comment I tried to solve your problem :-)
—SA
Sergey Alexandrovich Kryukov at 21-Jan-13 17:08pm
   
At too looping through types, it is 1) possible, 2) not required, due to the practical shortcut with assembly attribute I described, 3) desired or not — it depends.
—SA
Naerling at 21-Jan-13 17:31pm
   
Well, you make all valid points here. I'm going to accept your answer.
Thanks for the interesting discussion :-)
Sergey Alexandrovich Kryukov at 21-Jan-13 17:44pm
   
Thank you, too. :-)
—SA
Naerling at 22-Jan-13 9:55am
   
Well, Sergey, today I finally found a solution to my problem. I went for a different solution, which is: whenever a HeaderBarComponentBase descendant is dragged onto a Form designer the HeaderBarComponent is automatically put into the HeaderBar (whatever that may be).
 
I gave my HeaderBarComponentBase a Designer (Inherits System.Windows.Forms.Design.ControlDesigner) and override the following methods as follows.
I just thought I'd share the solution since you put quite some effort into it as well and might be interested in the solution :-)
 
private IComponent _host;
 
public override void InitializeNewComponent(System.Collections.IDictionary defaultValues)
{
defaultValues["Parent"] = _host;
base.InitializeNewComponent(defaultValues);
}
 
public override void Initialize(IComponent component)
{
base.Initialize(component);
this.FindHeaderBarHost((component.Site.Container as IDesignerHost).RootComponent);
}
 
private void FindHeaderBarHost(IComponent parentComponent)
{
IHeaderBarComponentHost host = null;
Control parent = parentComponent as Control;
while (host == null && parent != null)
{
host = parent as IHeaderBarComponentHost;
parent = parent.Parent;
}
 
if (host != null)
{
host.HeaderBarComponents.Add(this.Control as HeaderBarComponentBase);
_host = host.HeaderBarComponentHost;
}
else { throw new InvalidOperationException("A headerbar component cannot be added to a container that does not implement IHeaderBarComponentHost."); }
}
Sergey Alexandrovich Kryukov at 22-Jan-13 10:30am
   
Interesting, anyway...
—SA
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 2

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.
  Permalink  
Comments
Sergey Alexandrovich Kryukov at 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)

  Print Answers RSS
0 CHill60 185
1 Sarvesh Kumar Gupta 185
2 OriginalGriff 179
3 George Jonsson 154
4 ArunRajendra 144


Advertise | Privacy | Mobile
Web04 | 2.8.140709.1 | Last Updated 18 Jan 2013
Copyright © CodeProject, 1999-2014
All Rights Reserved. Terms of Service
Layout: fixed | fluid