0

I have two distinct namespaces, each with a Thing class:

namespace First.Second.Third
{
    public class Thing { }
}

namespace Fourth.Fifth.Sixth
{
    public class Thing { }
}

Now I try to use Thing elsewhere, and of course the compiler complains due to the ambiguous reference to that class:

namespace ConsoleApp1
{
    using First.Second.Third;
    using Fourth.Fifth.Sixth;

    internal static class MainEntryPoint
    {
        internal static void Main(string[] args)
        {
            var x = new Thing(); // Complaint.
        }
    }
}

If I add an alias to one of the namespaces in the using directive, the compiler error goes away:

using MyAlias = First.Second.Third;
using Fourth.Fifth.Sixth;

Now the compiler thinks I'm referring to Fourth.Fifth.Sixth.Thing when I do var x = new Thing();.

Why does the compiler resolve the ambiguity simply by adding an alias to one of the namespaces? Why does it automatically pick the namespace that I did not alias?

I expected this to be well documented and covered many times before on SO and elsewhere, but I can't find the answer. Can someone help me find the dupe if there is one?

EDIT: I'm not on the same page as everyone I guess. Here's a real-world example that might help:

If I have two people named "Bob", and I just say "here, give this to Bob", you won't know whom to give it to. If I say, well one is "Bob Smith" and the other is "Bob Johnson", of course I can now say "here, give this to Bob Smith." Now, if I say, "let's call Bob Smith by a new name: Bob Brady". If I then say "here, give this to "Bob", that is still ambiguous. See my point?

rory.ap
  • 34,009
  • 10
  • 83
  • 174
  • Because compiler does not know which one of Thing do you want to use? – Samvel Petrosov Jun 13 '17 at 13:54
  • To reference First.Second.Third you must explicitly instantiate it as x.Thing, so there's no ambiguity thanks to the alias (you aren't using the namespace directly). – Gusman Jun 13 '17 at 13:55
  • I know that. I'm not asking how to use it. I'm asking why the ambiguity goes away. – rory.ap Jun 13 '17 at 13:56
  • @PaulF -- That's not it. I changed the alias. – rory.ap Jun 13 '17 at 13:57
  • 1
    You need `x.Thing` to get a thing from `First.Second.Third` and `Thing` without `x` refers to `Fourth.Fifth.Sixth.Thing` . They each have a unique name so no ambiguity. – apokryfos Jun 13 '17 at 13:57
  • Possible duplicate of [C# namespace alias - what's the point?](https://stackoverflow.com/questions/505262/c-sharp-namespace-alias-whats-the-point) – Samvel Petrosov Jun 13 '17 at 13:58
  • @apokryfos -- They already had a unique name: `First.Second.Third.Thing` and `Fourth.Fifth.Sixth.Thing`. Now, I have `MyAlias.Thing`, so why is it not ambiguous? It just has a new namespace "nickname". – rory.ap Jun 13 '17 at 13:59
  • 1
    You wanted to create it as `Thing` the compiler didn't know which one you meant. The alias fixed that. If you did `new First.Second.Third.Thing` it would have never complained in the first place. It stops being ambiguous because you "promised" the compiler that you will always refer to `First.Second.Third.Thing` as `MyAlias.Thing` – apokryfos Jun 13 '17 at 13:59
  • @apokryfos -- Please see my edit with real-world example that might demonstrate my confusion better. I know I'm missing something stupid. – rory.ap Jun 13 '17 at 14:20
  • 1
    @rory.ap Your real world example isn't quite the same thing, though. A more apt way of describing it would be "if I don't give you someone's last name, assume that it's 'Johnson' and if I use the last name 'Brady' I really mean 'Smith', now give me 'Bob'". Since you didn't specify the last name, the compiler assumes that it's 'Johnson' and gives you 'Bob Johnson', no ambiguity. If you didn't use the alias it'd be like saying "assume that if I don't give a last name it's either 'Johnson' or 'Smith'". – Kyle Jun 13 '17 at 14:25
  • @S.Petrosov -- That's talking about type aliases. I'm talking about namespace aliases. – rory.ap Jun 13 '17 at 14:26

4 Answers4

10

I think your misconception is that you believe that aliasing automatically imports the types of a namespace.

using Fourth.Fifth.Sixth;

makes all types in this namespace visible without additional qualification.

using MyAlias = First.Second.Third;

does nothing besides giving the namespace a new name, without importing it. So, if you change using First.Second.Third; into using MyAlias = First.Second.Third; the ambiguity is removed because Thing from First.Second.Third is no longer visible without further qualification. But obviously the other using is still importing Thing from Fourth.Fifth.Sixth.

Also see the corresponding definitions in the C# 5.0 Language Specification:

A using-alias-directive (§9.4.1) introduces an alias for a namespace or type.

A using-namespace-directive (§9.4.2) imports the type members of a namespace.

adjan
  • 13,371
  • 2
  • 31
  • 48
  • Hmmm...now we're on to something. Can you point to where this documented? I'd just like to read the rules. I'm reading through the C# spec but can't find it. – rory.ap Jun 13 '17 at 14:05
  • See first sentence : https://msdn.microsoft.com/en-us/library/aa664765(v=vs.71).aspx – PaulF Jun 13 '17 at 14:07
  • I've read that already. It doesn't get to your point about importing vs. not importing. Why does giving the namespace a nickname (alias) remove ambiguity between two identically-named types they both have? – rory.ap Jun 13 '17 at 14:10
  • 1
    @rory.ap they are not identically named w.r.t. fully qualified names. That's why there's no ambiguity. – apokryfos Jun 13 '17 at 14:12
  • @apokryfos -- If I have two people named "Bob", and I just say "here, give this to Bob", you won't know whom to give it to. If I say, well one is "Bob Smith" and the other is "Bob Johnson", of course I can now say "here, give this to Bob Smith." Now, if I say, "let's call Bob Smith by a new name: Bob Blah Blah". If I say "here, give this to "Bob", that is still ambiguous. See my point? – rory.ap Jun 13 '17 at 14:14
  • 1
    "A using-alias-directive introduces an identifier that serves as an alias for a namespace or type within the immediately enclosing compilation unit or namespace body." - So all that has been done is give a shorter name of MyAlias to First.Second.Third without importing it - so there is effectively only one using statement "Fourth.Fifth.Sixth", so no ambiguity. You can add "using First.Second.Third;" back into your code & the compiler will not tell you it is has already been used - but your ambiguity will be back. – PaulF Jun 13 '17 at 14:20
  • @rory.ap but when you make an alias you are saying "Bob smith" will only be called either "bob smith" or "bob blah blah" never just "bob" . That's why there's no ambiguity – apokryfos Jun 13 '17 at 14:23
  • @apokryfos okay so the key is that you not only give the namespace an alias, but you *have* to use that alias whenever you're using the types in that namespace. That's where I wasn't clear. – rory.ap Jun 13 '17 at 14:24
  • @apokryfos -- Further evidence of this: if I use a non-ambiguous type that only resides in the aliased namespace without using the alias before the type name, it says it cannot resolve the reference. In other words, I always need to use the alias. – rory.ap Jun 13 '17 at 14:32
  • You can use both with and without alias - if First.Second.Third contained another class called Thing2 not in the other namespace you could do something like _"using MyAlias = First.Second.Third; using First.Second.Third;using Fourth.Fifth.Sixth; var x = new MyAlias.Thing(); var y = new First.Second.Third.Thing(); var z = new Thing2();"_ The first two avoid the ambiguity, no ambiguity in the third. – PaulF Jun 13 '17 at 14:32
  • @rory.ap yes. Admittedly the docs only hint at this behaviour rather than have explicit examples, but there's some explanation there. – apokryfos Jun 13 '17 at 14:33
  • @adjan -- I found section 9.4 in the C# language spec (5.0). It states: A using-alias-directive (§9.4.1) introduces an alias for a namespace or type. A using-namespace-directive (§9.4.2) imports the type members of a namespace. That seems to be the best there is for a reference. If you include this in your answer, and maybe talk about what I said in my comment above to apokryfos ("Further evidence of this..."), I will accept your answer. – rory.ap Jun 13 '17 at 14:48
  • @rory.ap did so – adjan Jun 13 '17 at 16:33
2

Here's the thing, you do:

using First.Second.Third;
using Fourth.Fifth.Sixth;

Now everything in First.Second.Third and Fourth.Fifth.Sixth can be referenced without their fully qualified name. This includes Thing however since both contain Thing then doing new Thing() is ambiguous.

If you do:

using First.Second.Third;
using MySixth = Fourth.Fifth.Sixth;

Then only First.Second.Third can be referenced without their fully qualified name, the namespace Fourth.Fifth.Sixth is just also known as MySixth, therefore no ambiguity.

Note the wording in: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-directive

For the first case it does say:

using System.Text;

To allow the use of types in a namespace so that you do not have to qualify the use of a type in that namespace

For the alias it does not mention qualifying of namespace use:

using Project = PC.MyCompany.Project;

To create an alias for a namespace or a type

Community
  • 1
  • 1
apokryfos
  • 38,771
  • 9
  • 70
  • 114
0

I believe you're confusing the x alias with the x variable. Since you use x for alias in First.Second.Third if you want to use the Thing from that then you should use

var aThing = new x.Thing();

HTH, Mike

Mike
  • 788
  • 10
  • 27
  • See my update. I changed the alias name, didn't fix anything. Same question. – rory.ap Jun 13 '17 at 13:59
  • see @mjwills answer. If you use Thing() without the alias name then it assumes you mean the one that's not aliased. – Mike Jun 13 '17 at 14:02
0

By putting an alias on the first option, you have effectively resolved the ambiguity (there aren't two Thing now - there is a Thing and a MyAlias.Thing).

If you refer to Thing it will default to using the non-aliased version (unless you specifically use the alias). This is exactly the same way that Console.ReadLine works if you use using System.

See below.

namespace Test
{
    using MyAlias = First.Second.Third;
    using Fourth.Fifth.Sixth;

    public class Program
    {
        static void Main()
        {
            var x = new Thing(); // non aliased
            var y = new MyAlias.Thing(); // aliased
            Console.ReadLine();
        }

    }
}

See also:

mjwills
  • 23,389
  • 6
  • 40
  • 63
  • I know it's defaulting. I stated that in my question. Why? Can you point me to some documentation? – rory.ap Jun 13 '17 at 14:01
  • Well it defaults to the non-aliased one for the same reason why if you have `use System;` then you can use `Console.ReadLine()` (i.e. you don't need to write `System.Console.ReadLine()`) – mjwills Jun 13 '17 at 14:05
  • I just tried this in one of my programs and the act of adding the alias made it show red wherever that item was used. Creating the alias not only allows you to use the alias name, it requires it. Essentially First.Second.Third is not visible to the compiler. – Mike Jun 13 '17 at 14:09
  • First.Second.Third **is** visible to the compiler - you just need to use the `MyAlias` alias that is specified in my (and your) code sample. If I specify an alias, it is mandatory to include the alias. That is the whole point of using an alias. – mjwills Jun 13 '17 at 14:11
  • The point I am making is that your question is effectively the wrong way around. You are asking 'why does it choose the non-aliased one'. The point I am making is that how every other using statement (`System`, `System.Linq` etc) works - why would you expect this to be different? – mjwills Jun 13 '17 at 14:13
  • I meant that it's visible to the compiler *only* through the alias. And that's why we get the Forth.Fifth.Sixth when we use Thing() by itself. – Mike Jun 13 '17 at 14:14
  • Let's use an analogy. You have a workplace where you refer to people only by their first name. You have two Bobs there. How do you differentiate between them? You can't. So if someone asks for Bob you say 'sorry, can't help you - I don't know which Bob you want'. So you come up with a system. If someone asks for Bob - you point them to the first Bob. If they ask for Bob **Smith** - you point them to the second Bob. In that analogy, the introduction of **Smith** is enough to distinguish the two Bobs. **Smith** is effectively an alias. The compiler works much the same way. – mjwills Jun 13 '17 at 14:18
  • Now, if there were **three** Bobs, the third Bob would need a new alias (e.g. Bob **Jones**). Similar, if you add a third Thing under a third namespace, you'd need a second alias. There would be Bob (unaliased), Bob (aliased by Smith) and Bob (aliased by Jones). – mjwills Jun 13 '17 at 14:19
  • @mjwills -- Wow, I independently came up with an analogy using Bob Smith before reading your last comment (see my question edit). At any rate, my question is, why does it pick the "first" bob arbitrarily? – rory.ap Jun 13 '17 at 14:20
  • For the exact same reason that `Console.WriteLine` works if you have `using System`. Take away `using System` and you need to use `System.Console.WriteLine`. The exact same principle applies with Thing. **Because that is what an unaliased using statement does.** – mjwills Jun 13 '17 at 14:21
  • And hilarious that we both came up with Bob Smith independently! Haha! – mjwills Jun 13 '17 at 14:22
  • @rory.ap To be clear, it doesn't pick it *arbitrarily*. It picks it because you have an unaliased using statement that refers to it. The same way it picked `System.Console` when I wrote `Console.WriteLine`. – mjwills Jun 13 '17 at 14:26