Problems with IsSubclassOf

P

Paguro Bernardo

Hi all,

I'm building a user control in c#. This control has a property which
can contain an instance of a user defined class which has to be a
subclass of an abstract class A. I'd like the user of the control to be
able at design time to select the value of this property from a
dropdownlist showing all the subclasses of A that can be found in the
assemblies referenced in the project, so I wrote my own UITypeEditor
and at first everything seemed to be fine.
But then I noticed that the list in my editor displays only the
subclasses of A that are contained in assemblies which were referenced
before the control was added to the form, while it ignores the ones
contained in assemblies added after the creation of the control.
I found out that the even the classes contained in these late
assemblies are correctly identified by my code, but that the result of
the IsSubclassOf method is false instead of true.
This is a sample of what I'm doing (rs is an ITypeResolutionService):

Solution sol = currentDTE.Solution;
aList=new ArrayList();
foreach (Project prj in sol.Projects)
{
VSProject vsPrj=(VSProject) prj.Object;
References refs = vsPrj.References;
foreach (Reference aRef in refs)
{
AssemblyName aName=new AssemblyName();
aName.Name=aRef.Name;
Assembly anAssembly=rs.GetAssembly(aName);
if (anAssembly!=null)
{
Type[] types=anAssembly.GetTypes();
foreach (Type t in types)
{
if (t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(A)))
aList.Add(t.FullName);
}
}
}
}

Can you tell me what I'm doing wrong?

Many thanks,

Francesco.
 
J

Jon Skeet [C# MVP]

Paguro said:
I'm building a user control in c#. This control has a property which
can contain an instance of a user defined class which has to be a
subclass of an abstract class A. I'd like the user of the control to be
able at design time to select the value of this property from a
dropdownlist showing all the subclasses of A that can be found in the
assemblies referenced in the project, so I wrote my own UITypeEditor
and at first everything seemed to be fine.

Do you have class A defined in multiple assemblies by any chance?
That's usually the problem.

Jon
 
P

Paguro Bernardo

Jon Skeet [C# MVP] ha scritto:
Do you have class A defined in multiple assemblies by any chance?
That's usually the problem.

Jon

Wow, faster than light!

It seems to me that class A is defined in just a single assembly, which
is referenced by three different assemblies: the one defining its
subclasses, the one defining the UITypeEditor, and the one defining the
user control. All these assemblies are located in the same directory,
and all of them are referenced in the project where I'm testing this
stuff. Can this be a problem?

Thanks again,
Francesco.
 
J

Jon Skeet [C# MVP]

Paguro said:
It seems to me that class A is defined in just a single assembly, which
is referenced by three different assemblies: the one defining its
subclasses, the one defining the UITypeEditor, and the one defining the
user control. All these assemblies are located in the same directory,
and all of them are referenced in the project where I'm testing this
stuff. Can this be a problem?

So all the assemblies are in the same directory at runtime? That should
be okay.

Could you post a short but complete example which demonstrates the
problem?
See http://www.pobox.com/~skeet/csharp/complete.html for what I mean by
that.

You might want to go into the debugger having got both typeof(A) and
myType.BaseType into separate variables, and compare the two of them
for equality.

Jon
 
P

Paguro Bernardo

Jon Skeet [C# MVP] ha scritto:
So all the assemblies are in the same directory at runtime? That should
be okay.
Well, not exactly at runtime. I'm trying to add a control to a form at
design time, and the inheritance test is done when editing one of the
control properties.
Could you post a short but complete example which demonstrates the
problem?
See http://www.pobox.com/~skeet/csharp/complete.html for what I mean by
that.
For the time being I can't reach that URL because a stupid proxy
categorizes it as Health, and I'm not allowed to reach it from the
office. I'll take a look at it later and will will post the example.
You might want to go into the debugger having got both typeof(A) and
myType.BaseType into separate variables, and compare the two of them
for equality.
That's not so easy for me, since I'm quite a beginner and I'm not able
to debug this kind of things yet (debugging something at design time is
still black magic to me).

Francesco.
 
J

Jon Skeet [C# MVP]

[Looks like my previous reply didn't make it - apologies if this shows
up twice]

Well, not exactly at runtime. I'm trying to add a control to a form at
design time, and the inheritance test is done when editing one of the
control properties.

Ouch. Okay - I don't know much about design-time stuff, I'm afraid.
For the time being I can't reach that URL because a stupid proxy
categorizes it as Health, and I'm not allowed to reach it from the
office. I'll take a look at it later and will will post the example.

Try going to http://www.yoda.arachsys.com/csharp/complete.html instead
- it's the same page, pobox.com is just a redirection service.
That's not so easy for me, since I'm quite a beginner and I'm not able
to debug this kind of things yet (debugging something at design time is
still black magic to me).

In that case, I suggest you work out what the interesting information
might be, and make your class write it out to a log (preferrably a
plain text file somewhere, so it's easy to work out whether or not it's
working). It's a bit slow and painful, but may well be easier than
trying to do design-time debugging.

Jon
 
J

Jon Skeet [C# MVP]

Paguro said:
Well, not exactly at runtime. I'm trying to add a control to a form at
design time, and the inheritance test is done when editing one of the
control properties.

Ah. Okay, that's likely to be entirely different then. Hmm.
For the time being I can't reach that URL because a stupid proxy
categorizes it as Health, and I'm not allowed to reach it from the
office. I'll take a look at it later and will will post the example.

Try http://www.yoda.arachsys.com/csharp/complete.html - pobox.com is
just a redirector.
That's not so easy for me, since I'm quite a beginner and I'm not able
to debug this kind of things yet (debugging something at design time is
still black magic to me).

That's understandable. I haven't done any design-time stuff at all, so
I'd probably be doing even worse.

You could cheat a little though - open a file and write log entries
with all the information you'd get from the debugger. It's a bit
painful, but should work.

Jon
 
P

Paguro Bernardo

Jon Skeet [C# MVP] ha scritto:
The redirector is OK, its yoda the "health" site. Anyway, in the
evening the damn thing is down, so I was able to reach your page. It's
always nice to meet another Catan's settler.

This is exactly what I'm trying to do:

I've got the following assemblies:
------------------------------------------------------------------------------------
the assembly MyBaseClass defining just the following class:

using System;

namespace MyBaseClass
{
public class BaseClass
{
public BaseClass()
{
}
}
}
------------------------------------------------------------------------------------
the assembly MyDerivedClasses which references MyBaseClass and contains
2 derived classes: DerivedA and DerivedB:


using System;
using MyBaseClass;

namespace MyDerivedClasses
{
public class DerivedA : BaseClass
{
public DerivedA()
{
}
}

public class DerivedB : BaseClass
{
public DerivedB()
{
}
}
}
--------------------------------------------------------------------------------------------------
the assembly MyTypeEditor, which references MyBaseClass and the System
assemblies System, System.Drawing.Design, System.Windows.Forms.Design,
System.Windows.Forms, System.ComponentModel.Design, System.Reflection,
EnvDTE, VSLangProj.
This assembly defines MyEditor as follows:

using System;
using System.Drawing.Design;
using System.Windows.Forms.Design;
using System.Windows.Forms;
using System.ComponentModel.Design;
using System.Reflection;
using EnvDTE;
using VSLangProj;
using MyBaseClass;


namespace MyTypeEditor
{
public class MyEditor : UITypeEditor
{
IWindowsFormsEditorService edSvc;
ListBox lista;

public MyEditor()
{
}

public override UITypeEditorEditStyle GetEditStyle(
System.ComponentModel.ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.DropDown;
}

public override object
EditValue(System.ComponentModel.ITypeDescriptorContext context,
System.IServiceProvider provider, object value)
{
ITypeResolutionService rs;
DTE currentDTE;

edSvc
=(IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
rs =
(ITypeResolutionService)context.GetService(typeof(ITypeResolutionService));
currentDTE = (DTE)context.GetService(typeof(DTE));

Solution sol = currentDTE.Solution;
lista=new ListBox();
foreach (Project prj in sol.Projects)
{
VSProject vsPrj=(VSProject) prj.Object;
References refs = vsPrj.References;
foreach (Reference aRef in refs)
{
if (!aRef.Name.StartsWith("System"))
{
AssemblyName aName=new AssemblyName();
aName.Name=aRef.Name;
Assembly a=rs.GetAssembly(aName);
if (a!=null)
{
Type[] types=a.GetTypes();
foreach (Type t in types)
{// this MessageBox added to
understand what's happening at design time The problem occurs at this
point
MessageBox.Show(t.Name +" is
derived from BaseClass: " +
t.IsSubclassOf(typeof(BaseClass)).ToString());
if
(t.IsSubclassOf(typeof(BaseClass)))

lista.Items.Add(t.FullName);
}
}
}
}
}
try
{
lista.SelectedIndexChanged +=new
EventHandler(lista_SelectedIndexChanged);
edSvc.DropDownControl(lista);
return lista.SelectedItem;
}
catch
{
return value;
}
finally
{
lista.Dispose();
}
return value;
}

private void lista_SelectedIndexChanged(object sender,
EventArgs e)
{
if (edSvc != null) edSvc.CloseDropDown();
}

}
}
-----------------------------------------------------------------------------------------------------------
Finally there is the assembly MyTestControl which defines the
TestControl UserControl (a very simple control containing only a
TextBox and a string property which uses MyEditor as UITypeEditor) and
references MyTypeEditor:

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Drawing.Design;
using TestListConverter;

namespace MyTestControl
{
public class TestControl : System.Windows.Forms.UserControl
{
private String ps;

[Editor(typeof(UnEditor),typeof(UITypeEditor))]
public String s
{
get {return ps;}
set {ps = value;}
}

private System.Windows.Forms.TextBox textBox1;
private System.ComponentModel.Container components = null;

public TestControl()
{
InitializeComponent();
}

protected override void Dispose( bool disposing )
{
if( disposing )
{
if( components != null )
components.Dispose();
}
base.Dispose( disposing );
}

private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
this.textBox1.Location = new System.Drawing.Point(24,
16);
this.textBox1.Name = "textBox1";
this.textBox1.TabIndex = 0;
this.textBox1.Text = "textBox1";
this.Controls.Add(this.textBox1);
this.Name = "TestControl";
this.Size = new System.Drawing.Size(144, 56);
this.ResumeLayout(false);
}
}
}

---------------------------------------------------------------------------------------------------

At this point I open a new Windows Application Project with references
to MyBaseClass and MyTestControl. At this point if I FIRST add a
reference to MyDerivedClasses, and THEN add a TestControl to the form
everything seems to work fine when I try to set te s property of the
control using the editor. If I change the order of these steps (i.e. I
FIRST add the control, and THEN the reference) DerivedA and DerivedB
appear not to be derived from BaseClass.

Any idea?

Thanks again.

Francesco.
 
J

Jon Skeet [C# MVP]

Paguro Bernardo wrote:

At this point I open a new Windows Application Project with references
to MyBaseClass and MyTestControl. At this point if I FIRST add a
reference to MyDerivedClasses, and THEN add a TestControl to the form
everything seems to work fine when I try to set te s property of the
control using the editor. If I change the order of these steps (i.e. I
FIRST add the control, and THEN the reference) DerivedA and DerivedB
appear not to be derived from BaseClass.

Any idea?

Hmm.. I wouldn't like to say for sure, to be honest. I suspect if you
delve deeper (using MessageBox.Show as a poor man's debugger) you'll
find it's loading the assembly containing BaseClass from different
places. However, I would suggest that as you've got a workaround, you
use that. It's probably simpler than trying to code around the problem.
Sorry not to give a more pleasant answer :(

Jon
 
P

Paguro Bernardo

Jon Skeet [C# MVP] ha scritto:
Hmm.. I wouldn't like to say for sure, to be honest. I suspect if you
delve deeper (using MessageBox.Show as a poor man's debugger) you'll
find it's loading the assembly containing BaseClass from different
places. However, I would suggest that as you've got a workaround, you
use that. It's probably simpler than trying to code around the problem.
Sorry not to give a more pleasant answer :(
I investigated a little more, but wasn't able to understand what's
happening. Since we are developing a framework which will be used by 50
programmers, it's quite important to have something which is always
working in the same way and not depending on the order in which things
are done. So I found out that if I don't pass the type of the
superclass directly to IsSuperclassOf, but instead use
ITypeResolutionService to get the assembly and then the type starting
from their names, everything seems to be OK. I don't like it too much,
but it can be considered accettable until I understand better were the
assemblies really come from.

Thanks for your help.

Francesco.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top