目次

キーワード

概要

名前空間(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#の名前空間の区切りには「 . 」を使います)は画像や音声のエンコード形式ではなくテキストの文字コードだと容易に見当が付きます。

ちなみに、名前空間に含まれない部分、ソースコードの一番上の部分をグローバル名前空間(global 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というように、名前空間をすべて指定した形式の名前を完全修飾名と言います。

また、いちいち完全修飾名を書かなくても済むように、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 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);
    }
}

詳しくは、「静的メンバー」を参照。

エイリアス

先ほど自作した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=0; i<100; ++i)
      {
        fact *= i; ++i; fact *= i; ++i;
        x *= xx;
        sin += x / fact;
      }
      return sin;
    }//Sin
  }//class Math
}//namespace Ufcpp

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},            {1:f6}\n", x, y, z);
      }
    }
  }//class NameSpaceSample
}//namespace Sample
   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

エイリアス修飾子

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 上での外部エイリアス設定。

サンプル: https://github.com/ufcpp/UfcppSample/tree/master/Chapters/StructuredProgramming/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"); }
}

更新履歴

ブログ