目次

キーワード

概要

名前空間(name space)とは、 ファイルを種類ごとにフォルダに分けて管理するのと同じように、 クラスを種類ごとに分けて管理するための機構です。

ポイント
  • namespace キーワードで名前空間を定義します。

  • フォルダを掘ってファイルを整理するような感覚で、名前空間を作ってクラスを整理します。

  • 例: namespace SampleNameSpace { class SampleClass {} }

名前空間とは

名前空間は、ファイル整理のためにフォルダ分けすることに例えられます。

例えば、ウェブページを作成する場合、コンテンツごとにフォルダに分けて管理すると、サイトの管理がしやすくなります。 例えば、うちのサイトの場合、以下のようなフォルダ構成になっています。 (注:今は構成が変わっています。昔はこういう構成でした。)

/--+-- memo           (ブログ的な何か)
   |
   +-- csharp         (このコーナー)
   |
   +-- study-------+  (院試勉強まとめ用)
                   |
                   +-- em      (電磁理論)
                   |
                   +-- math    (数学)

そして各フォルダの中にhtmlや画像ファイルがあります。 このようにコンテンツごとに分けることで、どこにどのファイルがあるのかが分かりやすくなりますし、 それぞれのフォルダに同じ名前のファイル(例えばindex.htmlやback.png)があっても問題はおきません。

プログラムを作成する場合でも、プログラムの規模が大きくなってきて、クラスの数が多くなってくると、 クラスを関連性のあるもの同士まとめて管理するような仕組みが必要になってきます。 そのような、クラスを階層的に分類するための機構が名前空間です。

例として、.NET frameworkの標準クラスライブラリを見てみましょう。 .NET frameworkの標準クラスライブラリ中のクラスの大半はSystemという名前空間に属しています。 System名前空間の下に、TextIODrawingなどの名前空間があります。 以下に、名前空間の階層構造と、各名前空間の説明および名前空間に属するクラスの一部を簡単に示します。

System --+
         |
         +-- IO
         |   (ファイル入出力。File や Directory などが属する。)
         +-- Text -----+  (文章処理。Encoding などが属する。)
         |             |
         |             +-- RegularExpressions
         |                 (正規表現。Regex や Match などが属する。)
         |
         +-- Drawing --+  (GUI処理。Image や Font や Icon などが属する。)
                       |
                       +-- Imaging
                       |   (画像処理。ImageFormat や Encoder などが属する。)
                       +-- Printing
                           (印刷。PrintController などが属する。)

このように階層的に名前を管理することで、例えば、System.Text.Encodingクラス(Windowsのファイルシステムではフォルダの区切りに「 \ 」を使いますが、C#の名前空間の区切りには「 . 」を使います)は画像や音声のエンコード形式ではなくテキストの文字コードだと容易に見当が付きます。

C# では、名前空間の定義(= フォルダーを掘るようなものに) namespace キーワードを使います。

namespace MyNamespace // ← MyNamespace という名前空間(フォルダーみたいなもの)を掘った状態
{
    // その中にクラスを置く
    class X { }
}

一方で、「パスを通す」(フルネームで書かなくても FileRegex だけでクラスなどを参照する)ための構文も持っていて、こちらには using キーワードを使います。

using System;
using System.IO;

// System.IO の中に Directory がある。
// フルネームで書くなら System.IO.Directory.GetFiles()
var count = Directory.GetFiles(".").Length;

// System の中に Console がある。
// フルネームで書くなら System.Console()
Console.WriteLine($"フォルダーの下に {count} 個のファイルがあります");

ちなみに、名前空間に含まれない部分、ソースコードの一番上の部分をグローバル名前空間(global namespace)と呼びます。

// この辺りの事を「グローバル」(global)と呼ぶ。

namespace MyNamespace
{
    // この辺りは「名前空間の中」。
}

名前空間の使い方

具体的に名前空間を使う方法を見ていきましょう。 ここでは例として、学校の課題で文字列クラス、リストクラス、可変長配列クラス、画像クラスを作れといわれたとします(これらのものは、標準ライブラリに初めから用意されていますが、プログラムの勉強のためにわざわざ自作してみることになった)。

namespace (名前空間の定義)

まず、課題を出された各人の作ったクラスの名前が重ならないように、それそれ自分の名前を使って名前空間を作ります。 文字列クラスStringはそのすぐ下に作りましょう。 そして、リストクラスListと可変長配列クラスVectorは、名前空間Collectionsを作ってその下に、画像クラスImageは名前空間Drawingを作ってその下に作ることにします。 階層構造は以下のようになります。

Ufcpp --+-- String                    (文字列クラス)
        |
        +-- Collections --+-- List    (リストクラス)
        |                 |
        |                 +-- Vector  (可変長配列クラス)
        |
        +-- Drawing --------- Image   (画像クラス)

このような構造の名前空間を作るためには以下のように書きます。

namespace Ufcpp
{
  class String{// String の内容}

  namespace Collections
  {
    class List{// List の内容}

    class Vector{// Vector の内容}
  }

  namespace Drawing
  {
    class Image{// Image の内容}
  }
}

名前空間を定義するためには namespace というキーワードを使います。 そしてその後に続く {} の中で定義したクラスや名前空間はすべてその名前空間に属することになります。 また、以下のように書いてもこれとまったく同じ意味になります。

namespace Ufcpp
{
  class String{// String の内容}
}

namespace Ufcpp.Collections
{
  class List{// List の内容}
}

namespace Ufcpp.Collections
{
  class Vector{// Vector の内容}
}

namespace Ufcpp.Drawing
{
  class Image{// Image の内容}
}

つまり、名前空間を2つ以上の場所に分けて書くこともできますし、 「 . 」で区切ることで階層構造を指定できます。

次に、名前空間中に定義したクラスを参照する方法を説明します。 名前空間中に定義したクラスは、以下のように、階層構造を「 . 」で区切って指定することで参照できます。

class NameSpaceTest
{
  static void Main()
  {
    Ufcpp.String str = new Ufcpp.String("test");

    Ufcpp.Collections.List list = new Ufcpp.Collections.List();
    Ufcpp.Collections.Vector vec = new Ufcpp.Collections.Vector();

    Ufcpp.Drawing.Image image = new Ufcpp.Drawing.Image("back.png");
  }
}

Ufcpp.Collections.Vectorというように、名前空間をすべて指定した形式の名前を完全修飾名(fully qualified name)と言います。

ファイル スコープ namespace

Ver. 10

C# 10.0 から {} なしの以下のような書き方で名前空間を指定できるようになりました。

namespace Namespace;

class A { }

これで以下のコードと同じ意味になります。

namespace Namespace
{
    class A { }
}

新しい {} なしで ; を書いてしまう書き方はファイル全体を namespace {} でくくったのを同じ意味になります。 そういう意味でこの書き方をファイル スコープ名前空間(file-scoped namespace)と言います。

ファイル スコープ名前空間は1つの C# ファイルにつき1つだけ書けます。例えば以下のコードはコンパイル エラーになります。

namespace Ns1;
namespace Ns2;

class A { }

また、ファイル スコープ名前空間はファイルの「ほぼ先頭」に書く必要があります。 ファイル スコープ名前空間よりも前に書けるものはかなり限られていて、

くらいです。このうち頻繁に利用するのはコメントと using くらいでしょう。

// コメントと using は namespace よりも前に書ける。
using System.Text;

namespace Ns1;

// using は後にも書ける。
using System.Text.Encodings;

class A { }

これで以下のコードと同じ意味になります。

// コメントと using は namespace よりも前に書ける。
using System.Text;

namespace Ns1
{
    // using は後にも書ける。
    using System.Text.Encodings;

    class A { }
}

「インデントが1段減る」程度の小さなメリットですが、 一方でデメリットも「1ファイルに1つしか書けない」程度で、 ほとんどの人は制限を掛けられなくても最初から「1ファイルに1つしか書かない」ので特に問題にはならないでしょう。

using (名前空間の参照)

また、いちいち完全修飾名を書かなくても済むように、using ディレクティブというものが用意されています。

using Ufcpp; // 名前空間 Ufcpp 内にあるクラスを修飾名なしで使えるようになる

class NameSpaceTest
{
  static void Main()
  {
    String str = new String("test"); // Ufcpp. が要らない

    Drawing.Image image = new Drawing.Image("back.png");
  }
}
using Ufcpp;
using Ufcpp.Collections;
using Ufcpp.Drawing;

class NameSpaceTest
{
  static void Main()
  {
    String str = new String("test");     // Ufcpp. が要らない

    List list = new List();              // Ufcpp.Collections も要らない
    Vector vec = new Vector();

    Image image = new Image("back.png"); // Ufcpp.Drawing. も要らない
  }
}

先頭の using から始まる行がusingディレクティブです。 このように、usingディレクティブを使うことでコードの入力手間を省くことが出来ます。

ちなみに、 using ディレクティブはほぼファイルの先頭、もしくは、名前空間内の先頭にしか書けません。 (ファイルの先頭も「グローバル名前空間の先頭」という扱いなので、「名前空間内の先頭にだけ書ける」と考えて大丈夫です。) using ディレクティブよりも前に書けるのは、 コメントや空白のようにプログラムに影響しないものか、 プリプロセッサーextern aliasなどのめったに使わない構文だけです。

// (コメントを除いて) using より前にはほぼ何も書けない。
using System;

Console.WriteLine(); // 何か書いてしまうと…

using System.IO; // この行はコンパイル エラー。

ただ、名前空間自体が入れ子に書けるので、「名前空間の先頭にしか書けない」といっても using ディレクティブも入れ子で書けます。

using System;

namespace Ns1
{
    using System.IO;

    namespace Ns2
    {
        using System.Collections;
    }
}

また「using しすぎ」にはそこそこ注意が必要です。 名前の衝突を避けるために名前空間を掘っているのに、using するとその「名前空間分け」をなくすことになります。 例えば、以下のように「別名前空間の同名の型」を用意します。

// 名前空間違いで同じ名前のクラスを用意しておく。
namespace A
{
    class X { }
}

namespace B
{
    class X { }
}

ここで、using Ausing B を同時に書いてしまうと「どちらかわからない」というコンパイル エラーを起こします。 (こういうエラーを「名前があいまい」(ambiguous)と言います。)

// A と B の using を同列に並べる。
using A;
using B;

class C
{
    X x; // A.X か B.X かわからないのでエラー。
}

ちなみに、後述しますが、 入れ子の場合は内側優先で名前解決します。

global using

Ver. 10

C# 10.0 から using ディレクティブの前に global という修飾を付けることで、 プロジェクト内全域に対して影響を及ぼす using (名前空間の参照)ができるようになりました。 (これを global using ディレクティブといいます。 俗称としては単に「global using」。)

例えば、プロジェクト内のどこか1つのファイルに以下のようなコードを書いたとします。

global using System.Text.RegularExpressions;

これで、このプロジェクト内のすべてのファイルで、ファイルの先頭に using System.Text.RegularExpressions を書いたのと同じ状態になります。

例えば別のファイルに以下のようなコードを書いたとき、

var line = Console.ReadLine();
var m = Regex.Match(line, @"\d+");
if (m.Success)
    Console.WriteLine(m.Value);

以下のコードと同じ扱いでコンパイルされます。 (この例の場合、Regex クラスが System.Text.RegularExpressions 名前空間内で定義されいているクラスなので、using System.Text.RegularExpressions が必要。)

using System.Text.RegularExpressions;

var line = Console.ReadLine();
var m = Regex.Match(line, @"\d+");
if (m.Success)
    Console.WriteLine(m.Value);

同じキーワードを流用したため後述する global エイリアスと紛らわしいですが別物です。

ちなみに、通常の using ディレクティブに加え、後述する using staticusing エイリアスに対しても同様に global 修飾を付けることでプロジェクト全域化できます。

global using System.Text.RegularExpressions;
global using static System.Linq.Enumerable;
global using Date = System.DateOnly;

global using は通常の using ディレクティブの前にしか書けません。 例えば以下のコードはコンパイル エラーになります。

using System;
global using System.Text.RegularExpressions;

using ディレクティブ自体が、ファイルの中でもかなり先頭の方にしか書けない構文なので、 必然的に global using よりも前に書けるものはほとんどなくなります。 ファイル スコープ名前空間よりもさらに厳しくて、

しか書けません。

global using の用途

前節で「using しすぎ」に注意を促しましたが、プロジェクト全域に影響を及ぼす global using ではなおの事注意が必要です。 基本的には「むやみやたらと使うものではない」という認識でいいと思います。

その一方で、System 名前空間(標準ライブラリの名前空間)のように、 世の中の C# コードの過半数が using していて、 「それはさすがに global using しても誰も困らないだろう」というものもあります。

実際、例えば .NET 5 (Visual Studio 2019) 時点で、Visual Studio でテンプレート通りに C# のクラスを作ると、 初期状態で以下のようなコードが作られます。 SystemSystem.Collections.Generic などの名前空間は「ほぼみんな使う」と判断されていて、初期状態で using が付いてきます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class A
    {
    }
}

これを、ファイル スコープ名前空間と併せて、 以下のようなコードにまでテンプレートの行数を減らしたいというのが global using の主な目的になります。

namespace ConsoleApp1;

class A
{
}

この場合でも、開発者自らが global using を書くことは少なくて、 実際には「自動的に生成されているもの」なことが多くなると思います。 詳しくはブログの「最初の C# プログラム」で説明しています。

補足: using static

Ver. 6

名前空間関連ではないんですが、名前空間の「using ディレクティブ」と似たものなのでここで紹介だけしておきたい機能が、 静的メソッドに対する 「using static」 です。 以下のように、静的メソッドの呼び出しに対して、クラス名を省略できるようになる機能です(C# 6からの機能)。

using System;
using static System.Math;

class Program
{
    static void Main()
    {
        var pi = 2 * Asin(1);
        Console.WriteLine(PI == pi);
    }
}

詳しくは、「静的メンバー」で説明します。

using エイリアス

先ほど自作したStringのテストのために、比較対象として.NET frameworkに標準で用意されているSystem.Stringクラスを同時に使用したいとします。 もちろん、Ufcpp.Stringというように完全修飾名を用いれば、System.Stringと共存可能なのですが、エイリアス(alias:別名付け)という機能を使うことでも共存させることが出来ます。

エイリアスは以下のような書き方をします。

using MyString = Ufcpp.String;

名前空間の先頭でこのような宣言をすることで、その名前空間中ではMyStringと書くことでUfcpp.Stringを参照することが出来ます。

using System;
using MyString = Ufcpp.String;           // クラスのエイリアス
using MyCollections = Ufcpp.Collections; // 名前空間のエイリアスも作れる

class NameSpaceTest
{
  static void Main()
  {
    String str = new String("test");
    //↑ System.String が参照される
    MyString str = new MyString("test");
    //↑ Ufcpp.String が参照される
    MyCollections.List list = new MyCollections.List();
    //↑ Ufcpp.Collections.List が参照される
  }
}
サンプル
using System;
 
/// <summary>
/// 自作クラス用の名前空間
/// </summary>
namespace Ufcpp
{
    /// <summary>
    /// 数学関数の自作
    /// </summary>
    public class Math
    {
        /// <summary>
        /// sin(x) の値を求める。
        /// この実装は甘い。
        /// 入力できる値は-0.1~0.1程度で、精度も4桁程度。
        /// </summary>
        public static double Sin(double x)
        {
            double xx = -x * x;
            double fact = 1;
            double sin = x;
 
            for (int i = 2; i < 100;)
            {
                fact *= i; ++i; fact *= i; ++i;
                x *= xx;
                sin += x / fact;
            }
            return sin;
        }
    }
}
 
namespace Sample
{
    using MyMath = Ufcpp.Math;
 
    class NameSpaceSample
    {
        static void Main()
        {
            Console.Write("   x, System.Math.Sin(x), Ufcpp.Math.Sin(x)\n");
            for (int i = 0; i < 10; ++i)
            {
                double x = 0.01 * i;
 
                double y = Math.Sin(x);   // System.Math.Sin呼び出し
                double z = MyMath.Sin(x); // Ufcpp.Math.Sin呼び出し
 
                Console.Write("{0:f2},           {1:f6},            {2:f6}\n", x, y, z);
            }
        }
    }
}
   x, System.Math.Sin(x), Ufcpp.Math.Sin(x)
0.00,           0.000000,            0.000000
0.01,           0.010000,            0.010000
0.02,           0.019999,            0.019999
0.03,           0.029996,            0.029996
0.04,           0.039989,            0.039989
0.05,           0.049979,            0.049979
0.06,           0.059964,            0.059964
0.07,           0.069943,            0.069943
0.08,           0.079915,            0.079915
0.09,           0.089879,            0.089879

任意の型に対する using エイリアス

Ver. 12

C# 12 から以下のようなコードをコンパイルできるようになりました。

using Primitive = int;
using Array = int[];
using Nullable = int?;
using Tuple = (int, int);

要するに以下の2点が改善点です。

  • int みたいなキーワードをそのまま using エイリアスの右辺に書けるようになった
  • 配列nullable 値型タプルなどを C# の専用構文を使って書けるようになった

C# 11 以前でも以下のように、キーワード・専用構文を使わない書き方はできていました。

using Primitive = System.Int32;
using Nullable = System.Nullable<System.Int32>;
using Tuple = System.ValueTuple<System.Int32, System.Int32>;
//※ 配列を書く手段はなかった

また、少々不可解なことに、以下のようなコードも C# 11 以前から書けていました。

using Primitive = System.ValueTuple<int>;
using Array = System.ValueTuple<int[]>;
using Nullable = System.ValueTuple<int?>;
using Tuple = System.ValueTuple<(int, int)>;

つまり、型引数(ジェネリック型 X<T>T の部分)であればこれまでも intint[] などが書けました。 C# 12 では、なぜか最上位レベルの時にだけかかっていた謎の制限を取り払ったことになります。 (実際、仕様書・実装ともに微々たる修正だったようです。)

ちなみに、C# 12 ではポインターや関数ポインターに対しても using エイリアスを使えるようになりました。 詳しくは「unsafe 型に対する using エイリアス」で説明します。

エイリアス修飾子

Ver. 2.0

前節で説明したとおり、 名前空間にはエイリアス(別名)を付けられます。

例えば、以下のように、ちょっと長めの名前空間名 Ufcpp.Test.Utilities に、 短いエイリアス Util を付けたとします。

namespace Ufcpp.Test.Utilities
{
  class Image {}
}

namespace TestNamespace
{
  using Util = Ufcpp.Test.Utilities; // エイリアスをつける。

  class Program
  {
    static void Main(string[] args)
    {
      Util.Image img = new Util.Image();
    }
  }
}

このコード自体には特に問題もなく、ちゃんとコンパイルが通ります。 ところが、このプログラムを修正していくうちに、ちょっとした問題が生じる可能性があります。 例えば、複数人で開発しているものとして、 自分以外の誰かが、TestNamespace 内に Util というクラスを作ってしまったとしましょう。

namespace Ufcpp.Test.Utilities
{
  class Image {}
}

namespace TestNamespace
{
  using Util = Ufcpp.Test.Utilities;

  class Program
  {
    static void Main(string[] args)
    {
      Util.Image img = new Util.Image();
    }
  }

  class Util {} // Util クラスを追加。エラーになる。
}

たったこれだけでこのコードはコンパイルエラーを起こします。 (エイリアス Util がクラス Util と衝突しましたと怒られるか、 Util と言う名前は既に存在しますと怒られるはず。)

この問題を緩和するため、C# 2.0 では、エイリアス修飾子というものが追加されました。 エイリアス修飾子は、Alias.Class という書き方の代わりに、 Alias::Class と言うように、: を2つ付けます。 このエイリアス修飾子 :: は、基本的には . と同じ結果を生みますが、 ただ、エイリアスの後ろにしか付けられないという制限があります。 このため、:: の付いている部分の直前はエイリアスであることが確定し、 エイリアスと同名のクラスが追加されても混乱が起こりません。

namespace Ufcpp.Test.Utilities
{
  class Image {}
}

namespace TestNamespace
{
  using Util = Ufcpp.Test.Utilities;

  class Program
  {
    static void Main(string[] args)
    {
      Util::Image img = new Util::Image();
      //↑ この Util はエイリアスの Util とみなされる。
    }
  }

  class Util {} // Util と同名のクラスがあっても OK。
}

global 名前空間エイリアス

Ver. 2.0

名前の付け方次第では、完全修飾名で書いても参照できない場合があります。 以下のように、名前空間の階層に同名の識別子がある場合です。

using static System.Console;

namespace X.Y
{
    class Program
    {
        static void Main()
        {
            // 単に Y って書くと、名前空間 X.Y の方の意味になる
            Y.F(); // コンパイル エラー。名前空間 Y に F がいない
        }
    }
}

class Y { public static void F() => WriteLine("class Y"); }

階層違いで同名のものがあることが原因なので、必ず最上位(グローバル名前空間)からたどる手段があれば解決します。 そのために使うのが、global名前空間エイリアスです。 以下のように、global::から書き始めれば、最上位から名前をたどれます。

using static System.Console;

namespace X.Y
{
    class Program
    {
        static void Main()
        {
            // global エイリアスを使えば、最上位から名前をたどれる
            global::Y.F();
        }
    }
}

class Y { public static void F() => WriteLine("class Y"); }

globalは、::の前でだけキーワード扱いされる文脈キーワードです。 その他の場面では、globalクラスを作ったり、globalという名前の名前空間を作ったり、参照したりもできます。

外部エイリアス

Ver. 2.0

C# 2.0 では、using を使ってエイリアスを定義する代わりに、 コンパイルオプションでエイリアスを付けることが可能になりました(外部エイリアス)。

外部エイリアスを使うにはまず、 ソースファイル中に extern alias という宣言を書きます。

extern alias X;

class Program
{
  static void Main(string[] args)
  {
    X::A a = new X::A();
  }
}

そして、ソースファイルのコンパイル時に、 以下のようなオプションを追加します。

csc /r:X=Ufcpp.dll Test.cs

これで、Ufcpp.dll というライブラリ中で定義された A というクラスを、 X::A という名前で参照できるようになります。

Visual Studio 上では、図1のように、参照しているライブラリのプロパティを開いて、エイリアス(aliases)の行を編集します。

Visual Studio 上での外部エイリアス設定。
Visual Studio 上での外部エイリアス設定。

サンプル: ExternAliasConsoleApplication

この外部エイリアスを使うと、2つの異なるライブラリに、完全に同名前空間・同名のクラスがあっても、参照し分けることができます。 例えば、上記のサンプルは以下のようなシナリオを想定したものです。

  • .NET 2.0 で LINQ を使うために、Enumerable クラスや Extension 属性を自作した(BackportEnumerable.dll)

  • その BackportEnumerable のテストのために、標準の LINQ と自作の LINQ を両方使って、実行結果を比べたい(ExternAliasConsoleApplication.exe)

以下のようなコードで呼び分けできます。

namespace UsingStandard
{
    using System.Linq;

    class Sample
    {
        public static void Run()
        {
            var x = new[] { 1, 2, 3, 4, 5 };
            var y = x.Where(i => (i & 1) != 0).Select(i => i * i); // 標準の LINQ
            Console.WriteLine(string.Join(", ", y));
        }
    }
}

namespace UsingBackport
{
    extern alias Backport; // コンパイル オプションで BackportEnumerable.dll を指定
    using Backport::System.Linq;

    class Sample
    {
        public static void Run()
        {
            var x = new[] { 1, 2, 3, 4, 5 };
            var y = x.Where(i => (i & 1) != 0).Select(i => i * i); // 自作のパックポート LINQ
            Console.WriteLine(string.Join(", ", y));
        }
    }
}

名前解決の優先度

名前空間によって、同じ名前のものを複数作れます。 その同じ名前のものを使い分けたければ、ちゃんと完全修飾名を使う方のが一番ですが、 一応、usingを並べた場合の優先度についても説明しておきます。

まず、usingの使い過ぎなどでどちらか判別できない状況になると、コンパイル エラーになります。

using static System.Console;
using A;
using B;

namespace MyApp
{
    class Program
    {
        static void Main()
        {
            Lib.F(); // コンパイル エラー。A, B 区別つかない
        }
    }
}

namespace A
{
    class Lib { public static void F() => WriteLine("A"); }
}
namespace B
{
    class Lib { public static void F() => WriteLine("B"); }
}

usingや型定義を書く場所によって優先度が付いています。 優先度違いのものであれば、優先度が高い方が選ばれ、コンパイルできます。 逆に、同優先度のものがあるとエラーになります。

優先度ですが、以下のように、使う場所に近いほど優先、直接的なものほど優先です。

using static System.Console;
using A;

// using よりは、直接定義されているものの方が優先 A < C, global
// エイリアスと型定義は同列 C = global
using Lib = C.Lib;
class Lib { public static void F() => WriteLine("global"); }

namespace MyApp
{
    using B; // 内側に using を書くと、外より優先 A, C, global < B

    // 同一名前空間内にあるものは1番高い優先度 B < MyApp
    class Lib { public static void F() => WriteLine("MyApp"); }

    class Program
    {
        static void Main()
        {
            // Lib は5つある
            // この場合 MyApp.Lib が使われる
            // 優先度 高 MyApp > B > global = C > A 低
            Lib.F();

            // ちゃんと呼び分けたければフルネームで書く
            A.Lib.F();
            B.Lib.F();
            C.Lib.F();
            MyApp.Lib.F();
            global::Lib.F();
        }
    }
}

namespace A
{
    class Lib { public static void F() => WriteLine("A"); }
}
namespace B
{
    class Lib { public static void F() => WriteLine("B"); }
}
namespace C
{
    class Lib { public static void F() => WriteLine("C"); }
}

更新履歴

ブログ