"internal" scope constructors used in references (VS 2002/2003 compiler bug?)

  • Thread starter Thread starter Michael Lang
  • Start date Start date
M

Michael Lang

This occurs with both Visual Studio 2002 and 2003, framework versions 1.0
and 1.1 respectively.

I have a DLL containing a class such as:

============== CODE ===================
namespace MiddleDLL
{
public class MyClass
{
private OtherDLL.SupportClass _cls;
public MyClass(string name)
{
_cls = new OtherDLL.SupportClass();
}
internal MyClass(OtherDLL.SupportClass cls)
{
_cls = cls;
}
... class utility code here ...
}
}
============== End CODE ================

.... where OtherDLL.SupportClass is defined in a separate DLL referenced by
the DLL containing MyClass (Lets refer to that as "MiddleDLL").

In a 3rd DLL (refer to it as "FinalDLL") I referencing only MiddleDLL, and
not OtherDLL, in which contains the following code:

============== CODE ===================
namespace FinalDLL
{
// used by NUnit for testing...
public void SomeFunc()
{
MiddleDLL.MyClass inst = new MiddleDLL.MyClass("someName");
inst.SomeMethod();
}
}
============== End CODE ================

Am I doing something wrong at this point? Besides my naming convention....
:)

The error I get during compilation of FinalDLL is:
"error CS0012: The type 'OtherDLL.SupportClass' is defined in an assembly
that is not referenced. You must add a reference to assembly
'OtherDLL.Interop'."

No public methods or properties contain any types contained in OtherDLL.
Only internal members reference types in OtherDLL. So why would the
FinalDLL require a reference to OtherDLL? I have not tried this in an
instance where OtherDLL was not a COM interop DLL. Would that make a
difference?

If I do not use the constructor, I am still able to use a class instance
without OtherDLL referenced. This is why I'm guessing it is a bug in the
compiler.

============== CODE ===================
namespace FinalDLL
{
// used by NUnit for testing...
public void SomeFunc(MiddleDLL.MyClass inst)
{
inst.SomeMethod(); //this compiles fine.
}
}
============== End CODE ================

Also, if I make the following changes it will compile and run properly:

============== CODE ===================
namespace MiddleDLL
{
public class MyClass
{
private OtherDLL.SupportClass _cls;
public static MyClass Create(string name)
{
return new MyClass(name);
}
public MyClass(string name){...} //unchanged
internal MyClass(OtherDLL.SupportClass cls){...} //unchanged
... class utility code here ...
}
.... next namespace (FinalDLL) in separate dll...
}
namespace FinalDLL
{
// used by NUnit for testing...
public void SomeFunc()
{
MiddleDLL.MyClass inst = new MiddleDLL.MyClass("someName");
inst.SomeMethod();
}
}
============== End CODE ================

So, is this a bug? or do I need to change how I structure code? If it is
wrong, then why? Creating a static method for all the public constructors
just seems wrong!

If anyone confirms this sounds like a bug, then I'll report it to MS. If
you see this MS personnel, please let me know If you are submitting it as a
bug for me.

Michael Lang
 
I mistyped the final example. It should have been that the following
works...

============== CODE ===================
namespace FinalDLL
{
// used by NUnit for testing...
public void SomeFunc()
{
MiddleDLL.MyClass inst = MiddleDLL.MyClass.Create("someName");
inst.SomeMethod();
}
}
============== End CODE ================
 
If the "MiddleDLL" code is going to work at runtime, it will need to call
methods in "OtherDLL", won't it?
 
it works. but that isn't the point. Why should I need to? Why should a
project have to reference every DLL that is referenced every DLL referenced
by this project? If this was really needed then your reference list would
be hundreds of items long. If a DLL is not directly needed by a particular
project, it should not have to reference it.

If a 3rd party uses my "MiddleDLL" project to create a "FinalDLL" project,
they will be confused as to what "SupportDLL" is since THEY don't use it for
anything.

Michael Lang, MCSD
 
yes, and that works without OtherDLL being referenced in MiddleDLL. Because
none of the public members of MiddleDLL expose any types in OtherDLL.
OtherDLL is for support of/used by MiddleDLL only. In the case of my real
application, it would be bad for the user to manipulate OtherDLL directly.

In reality OtherDLL is AutoCAD which is a product of AutoDesk. AutoCAD is a
COM application. MiddleDLL is a manager for AutoCAD called
"AcadManager.dll" that provides a better and more functional API than
AutoCAD does. It also implements workarounds in the cases where AutoCAD
does NOT work as advertised. Using the AutoCAD API directly could
potentially cause a little inconsitency in values I am caching in
AcadManager. I cache to avoid as many COM calls as possible. If the user
uses the UI of AutoCAD, AcadManager clears cached values appropriately. I
have not tested what happens if they manipulate AutoCad via the API.

I would rather users of AcadManager.dll did not know about the actual
AutoCAD API. Well they will know one exists, but they won't be tempted to
use both my AcadManager API and the AutoCAD API at the same time. Therefore
they should not reference AutoCAD in their project. They should only
reference AcadManager.dll

Does this make sense now? I have written a test solution with 3 projects
demonstrating the problem and have sent it to Microsoft for review. Would
you like to see it? If so, where can I post it or email it?

Michael Lang, MCSD
 
I have verified it does not have to do with COM interop. I created a simple
3 project solution with basically the code below and the same error occurs.
 
Michael Lang said:
I would rather users of AcadManager.dll did not know about the actual
AutoCAD API. Well they will know one exists, but they won't be tempted to
use both my AcadManager API and the AutoCAD API at the same time. Therefore
they should not reference AutoCAD in their project. They should only
reference AcadManager.dll

Does this make sense now? I have written a test solution with 3 projects
demonstrating the problem and have sent it to Microsoft for review. Would
you like to see it? If so, where can I post it or email it?

Having a reference to another DLL does not mean knowing about the API. It
means registering your interest in a particular version of that DLL, stored
at a particular location so that, at runtime, you get the version you need.

Now, how could FinalObject.dll even build if it doesn't have a reference to
the AutoCAD dll? Which version of the AutoCAD dll will it use? Where will it
come from? These questions are answered by the reference.
 
But FinalDLL does not use AutoCAD! It has no "interest" or "need" as you
put it it what version of AutoCAD is used. It uses AcadManager.
AcadManager uses AutoCAD. AcadManager stores information as to which
version of AutoCAD it will use, and where it will come from. AcadManager is
"interested" in a multiple version of AutoCAD. FinalDLL stores what version
of AcadManager it will use, and where it will come from. What if I swap
versions of AcadManager used by FinalDLL, and the new version uses a
different version of AutoCAD. I don't want FinalDLL to be written for any
particular version of AutoCAD, I want it written for a particular version of
AcadManager. AcadManager will support the same API going forward, I can't
guarantee that with AutoCAD since that is out of my control.

I provide AcadManager to other developers (including myself) who want a
consistent API across versions of AutoCAD. They don't want to be concerned
with what has changed from version to version of AutoCAD. If their code is
compiled against a particular version of AutoCAD they lose that flexibility.
AutoDesk does not take the precautions Microsoft does in preserving
backwards compatibility, such as Microsoft is doing between versions of the
..NET framework. The day AutoDesk decides to develop an API that is stable
from version to version, there will no longer be a need for AcadManager. In
which case I will be very happy!

Michael Lang, MCSD
 
By the way, FinalDLL DOES build without a reference to AutoCAD. There is
only a compile problem when I use a "public" constructor and there happens
to also be an "internal" constructor that contains an arguement of a type
defined in AutoCAD. If I got rid of the internal constructor with an
AutoCAD type in it, everything compiles and runs with no errors. This is
really the whole point of my message in the first place. Why does finalDLL
even recognize that there is an "internal" constructor on a class in another
DLL. "internal" is supposed to be hidden from outside the assembly.

Michael Lang, MCSD
 
You create a library project (DLL) that references 5 other utility DLL's,
who each reference another 5 utility DLL's. In this case "utility" meaning
it is functionality used by the referencer, not something exposed in the API
of the referencer. Then an application that uses your library. Do you
think that application needs to reference all 30 utility DLL's contained in
the reference hierarchy of your library? I have not found this to be the
case.

Michael Lang, MCSD
 
Back
Top