C# public members visibility of a class with no namespace
Asked Answered
B

2

8

I have a project that I publish as MyNuget package in a local feed. The project references a ThirdPartyNuget that has ThirdPartyExtensions public static class with public extension methods over IQueryable.

Now, in my main solution I reference MyNuget and DO NOT reference ThirdPartyNuget. Still, Visual Studio 2022 shows the extensions methods from ThirdPartyExtensions. Why?

I do not expect this as I consider ThirdPartyNuget the implementation detail of MyNuget.

The assemblies in ThirdPartyNuget are obfuscated and ThirdPartyExtensions class looks weird as it has no namespace. Here is what I see in the debugger:

typeof(ThirdPartyExtensions).Name == typeof(ThirdPartyExtensions).FullName == "ThirdPartyExtensions"
typeof(ThirdPartyExtensions).Namespace == null
typeof(ThirdPartyExtensions).GUID == {00000000-0000-0000-0000-000000000000}

What's the mechanics of this behavior?

How do I hide the visibility of ThirdPartyExtensions in my main solution?

Brundisium answered 26/3, 2022 at 8:1 Comment(1)
I think this should be blamed to the third party package, who defined the class in no namespace. And you can't fix it.Doublejointed
P
7

If you use PackageReference format for your package dependencies, PrivateAsset tag in your MyNuget .csproj may do the trick.
https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets

You might be using a dependency purely as a development harness and might not want to expose that to projects that will consume your package. In this scenario, you can use the PrivateAssets metadata to control this behavior.

<PackageReference Include="ThirdPartyNuget" Version="1.0.0">
    <PrivateAssets>compile</PrivateAssets>
</PackageReference>

Or you can use developmentDependency in packages.config as described in this question:
Don't include dependencies from packages.config file when creating NuGet package


In terms of question: "why public dependencies is the default way?", I would say: It makes life easier.

It's easier to consume packages with public dependencies. And it's easier to make them public if it's default behavior.
For example, many packages for web development depended on Newtonsoft.json. And to configure serialization, it was needed to expose it. Another opinion about that.
Probably guys from MS have some statistics about it. But I didn't see any articles about that. They only recommend it as the default way.


In terms of your case, I'am not sure that I correctly reproduced it.

In my test I've created three projects:
One subPackage with empty default namespace. enter image description here One package with reference to SubPackage with PrivateAssets attribute. enter image description here enter image description here And project, which references Package, but not references SubPackage. enter image description here It doesn't compile if I try to use extensions. But prints '2' in runtime, if I use GetNumber method.

And if I've understood question correctly it's the desired result.
But I'm not sure, will it work for your patched ThirdPartyNuget package or not.

Pence answered 30/3, 2022 at 16:58 Comment(2)
Thank you, Kote! Setting PrivateAssets flag removes NugetSubPack.dll from the bin output folder of the main package. So I can compile it but cannot use it, getting "System.IO.FileNotFoundException : Could not load file or assembly" at run time.Brundisium
@UserControl, thank you. Yes, the previous example was wrong, it didn't copied NugetSubPack.dll. The correct way is marking only compile assets as private. <PrivateAssets>compile</PrivateAssets>.Pence
S
2

If you don't declare a namespace for your class, then the default global namespace is used.The global namespace is the namespace that contains namespaces and types that are not declared inside a named namespace. See here.

You can see classes which are not have namespace using global keyword. The global keyword is the global namespace alias only when it's the left-hand identifier of the :: qualifier.For more information read here

In my main solution I reference MyNuget and DO NOT reference ThirdPartyNuget. BUT your MyNuget referenced to ThirdPartyNuget. It means, your project will be referenced to ThirdPartyNuget as well.

It is not possible to hide the visibility of extensions, BUT...

Your expectation and goal is looks strange. Hovewer, you can do something like this:

If your goal is: do not allowing to use that extensions method in your projects that uses MyNuget you can create the same extensions classes with empty namespace. For example, ThirdPartyNuget project has this extension (without namespace):

public static class ThirdPartyExtensions
{
    public static void DoSome(this string value)
    {
        //Do some
    }
}

Just create the same class in MyNuget and try to use this in your project. You will get ambiguous error from compiler :).

So, it will not hide it, but it will not allow to use this extension

Syncytium answered 30/3, 2022 at 6:58 Comment(2)
Hmm, interesting. But if a developer fixes the namespace conflict by introducing an alias he will still be able to call functions from ThirdPartyNuget, won't he?Brundisium
@Brundisium He cannot. Both of them are under the global namespace.Syncytium

© 2022 - 2024 — McMap. All rights reserved.