Can anyone explain the following behavior?
In summary, if you create multiple CLS compliant libraries in Visual Studio 2008 and have them share a common namespace root, a library referencing another library will require references to that library's references even though it doesn't consume them.
It's pretty difficult to explain in a single sentence, but here are steps to reproduce the behavior (pay close attention to the namespaces):
Create a library called LibraryA and add a a single class to that library:
namespace Ploeh
{
public abstract class Class1InLibraryA
{
}
}
Make sure that the library is CLS Compliant by adding [assembly: CLSCompliant(true)]
to AssemblyInfo.cs.
Create another library called LibraryB and reference LibraryA. Add the following classes to LibraryB:
namespace Ploeh.Samples
{
public class Class1InLibraryB : Class1InLibraryA
{
}
}
and
namespace Ploeh.Samples
{
public abstract class Class2InLibraryB
{
}
}
Make sure that LibraryB is also CLS Compliant.
Notice that Class1InLibraryB derives from a type in LibraryA, whereas Class2InLibraryB does not.
Now create a third library called LibraryC and reference LibraryB (but not LibraryA). Add the following class:
namespace Ploeh.Samples.LibraryC
{
public class Class1InLibraryC : Class2InLibraryB
{
}
}
This should still compile. Notice that Class1InLibraryC derives from the class in LibraryB that doesn't use any types from LibraryA.
Also notice that Class1InLibraryC is defined in a namespace that is part of the namespace hierarchy defined in LibraryB.
So far, LibraryC has no reference to LibraryA, and since it uses no types from LibraryA, the solution compiles.
Now make LibraryC CLS compliant as well. Suddenly, the solution no longer compiles, giving you this error message:
The type 'Ploeh.Class1InLibraryA' is defined in an assembly that is not referenced. You must add a reference to assembly 'Ploeh, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
You can make the solution compile again in one of the following ways:
It seems that there is some strange interplay between the namespace hierarchy and CLS compliance.
Solving this issue can be done by picking one of the options in the list above, but can anyone explain the reason behind this behavior?
I had a look into the official documents for the CLS (http://msdn.microsoft.com/en-us/netframework/aa569283.aspx), but my head exploded before I could find a simple answer.
But I think the basis is that the compiler, in order to verify the CLS compliance of LibraryC, needs to look into possible naming conflicts with LibraryA.
The compiler must verify all "parts of a type that are accessible or visible outside of the defining assembly" (CLS Rule 1).
Since public class Class1InLibraryC inherits Class2InLibraryB, it must verify the CLS compliance against LibraryA as well, in particular because "Ploeh.*" is now "in scope" for CLS Rule 5 "All names introduced in a CLS-compliant scope shall be distinct independent of kind".
Changing either the namespace of Class1InLibraryB or Class1InLibraryC so they become distinct seems to convince the compiler there is no chance for a name conflict anymore.
If you choose option (2), add the reference and compile, you'll see that the reference is not actually marked in the resulting assembly meta-data, so this is a compilation/verification-time dependency only.