3

I am wondering why this situation is not resulting in an ambiguous reference between types, one a class within our library and one an external struct. Consider the following-

External struct-

namespace External.Models
{
    public struct ProxyUser
    {
        public string Username { get; set; }
        public string Password { get; set; }
    }
}

Our class-

namespace MyApp.Configuration
{
    public class ProxyUser
    {
        public string Username { get; set; }
        public string Password { get; set; }
    }
}

Then the calling code approximates-

using External.Models;

namespace MyApp.Configuration.Factories
{
    sealed class Factory
    {
        public async Task<T> BuildConfiguration<T>()
        {
            var proxy = new ProxyUser { Username = username, Password = password }; // I would expect this to be ambiguous, but it references out class
        }
    }
}

This is a NetStandard 2.0 library if that makes a difference. Behavior is the same if I make our class a struct. Why isn't this ambiguous?

EDIT - Class is sealed, again if that makes a difference...

ANSWER

As @Sinatr mentioned, it appears to be related to this question- https://stackoverflow.com/a/25437586/14872099

This is a demonstration of the different behaviors I observed.

This resolves to our MyApp.Configuration.ProxyUser-

using External.Models;

namespace MyApp.Configuration.Factories
{
    sealed class Factory
    {
        public async Task<T> BuildConfiguration<T>()
        {
            var proxy = new ProxyUser { Username = username, Password = password }; // resolves to our class
        }
    }
}

This resolves to the external struct-

namespace MyApp.Configuration.Factories
{
    using External.Models;

    sealed class Factory
    {
        public async Task<T> BuildConfiguration<T>()
        {
            var proxy = new ProxyUser { Username = username, Password = password }; // resolves to external struct
        }
    }
}

Moving our class to another namespace creates ambiguity-

using External.Models;
using MyApp.Configuration.Models; // moved our class to this namespace

namespace MyApp.Configuration.Factories
{
    sealed class Factory
    {
        public async Task<T> BuildConfiguration<T>()
        {
            var proxy = new ProxyUser { Username = username, Password = password }; // this is ambiguous
        }
    }
}
tjack2006
  • 39
  • 3
  • 3
    Because you are inside the same namespace group. Both classes are inside the `MyApp.Configuration` namespace. – Borja Sanchidrián Monge Mar 25 '21 at 12:40
  • It makes sense why it is aware of our `ProxyUser` class, but not why this isn't ambiguous. Are you aware of anything documenting how ambiguity is determined? – tjack2006 Mar 25 '21 at 12:52
  • 1
    Do you get warnings? I am unable to find a topic regarding how compiler resolve best type to match. AFAIR local assembly types have precedence, see e.g. [this question](https://stackoverflow.com/q/25437199/1997232). – Sinatr Mar 25 '21 at 12:54
  • Most probably that is due to `using`s, you import only one namespace, so you don't get the errors. – Michał Turczyn Mar 25 '21 at 13:01
  • 1
    This answer seems to be it. Since the `using` statement is outside our namespace, it is in the global namespace and takes a backseat to our namespace. Moving the using statement inside our namespace causes it to reference the external struct. https://stackoverflow.com/a/25437586/14872099 – tjack2006 Mar 25 '21 at 13:03
  • Does this answer your question? [Same fully qualified classname in multiple assemblies](https://stackoverflow.com/questions/25437199/same-fully-qualified-classname-in-multiple-assemblies) – nalka Mar 25 '21 at 13:24

1 Answers1

2

Internal namespaces (i.e. inside the namespace brackets) get priority over external namespaces (i.e. using statements before the namespace block).

Note: "internal" and "external" aren't official definitions here, I just use these to easily distinguish between them.

Due to nesting, your namespace MyApp.Configuration.Factories block inherently also looks at any direct ancestors (MyApp.Configuration and MyApp), but not sibling namespaces (e.g. MyApp.Configuration.Services).

When a non-ambiguous reference is found in the internal namespaces, the system never bothers to check the external namespaces, and thus does not find a conflict. You'd only encounter a conflict if:

  • There are two valid options in the internal namespace, e.g. MyApp.Configuration.ProxyUser and MyApp.Configuration.Factories.ProxyUser
  • There are two valid options in the referenced external namespaces, e.g. External.Models.ProxyUser and Another.Namespace.ProxyUser (assuming both are referenced with using statements in this file)
Flater
  • 12,908
  • 4
  • 39
  • 62