.NETのアクセス修飾子には多くの種類がある。それらを使いこなすことで、クラスや構造体などのコンテナおよびそれらのメンバへのアクセスを適切に設定できる。
型とそのメンバにはアクセシビリティーのレベルがあり、それを指定するのがpublicやprivateといったアクセス修飾子(access modifier)だ。本稿ではアクセス修飾子の種類と違いについて解説する。なお、本稿ではpublic(C#)/Public(VB)など、先頭文字が大文字か小文字かだけの違いしかない場合にはC#の記述(先頭文字も小文字)を採用して記述する。
特定のトピックをすぐに知りたいという方は以下のリンクを活用してほしい。
なお、本稿に掲載したサンプルコードをそのまま試すにはVisual Studio 2017 Update 5(バージョン15.5)以降が必要である。
ご存じの方は読み飛ばしていただいて構わない。アクセス修飾子の理解にはアセンブリの知識も必要なので、簡単に説明する。
アセンブリとは、.NET Framework用に作成された(通常は1つの)バイナリファイル(exeファイルまたはdllファイル)を指す。Visual Studio上の1プロジェクトが1アセンブリになる*1。「アセンブリ内からだけアクセス可能」というのは、「プロジェクトの内部からだけアクセスできる」(=別のプロジェクトからはアクセスできない)という意味になる。
*1 複数のファイルにまたがったアセンブリ(マルチファイルアセンブリ)を作ることも可能ではある(「マルチファイル アセンブリ」を参照)。その場合、internal(C#)/Friend(VB)アクセス修飾子の範囲は複数のファイルにまたがることになる。
付けられるアクセス修飾子は、トップレベルコンテナとメンバとで異なる。まず、トップレベルコンテナから見ていこう。
トップレベルコンテナとは、入れ子にされていない型のことだ。名前空間の直下にある型ともいえる。ここで、型とはクラス/列挙型/構造体/インタフェースなどだ。
トップレベルコンテナのアクセシビリティーレベルは簡単だ。次に示す2項目だけである。
publicアクセス修飾子を付けたトップレベルコンテナは、そのアセンブリの外からもアクセスできる。既定、またはinternal(C#)/Friend(VB)アクセス修飾子を付けたトップレベルコンテナは、アセンブリの外からアクセスできなくなる(同じアセンブリ内だけからアクセス可能)。
例として、Visual Studio上に2つのコンソールアプリのプロジェクト「ProjectA」/「ProjectB」を作って試してみよう。
ProjectAプロジェクトには、3つのクラスを追加する(次のコード)。
namespace ProjectA
{
public class PublicClass
{}
internal class InternalClass
{}
class DefaultClass
{}
}
Public Class PublicClass
End Class
Friend Class InternalClass
End Class
Class DefaultClass
End Class
同じProjectAプロジェクトのMainメソッドからは、3つのクラスのどれにでもアクセスできる(次のコード)。同じアセンブリ内からのアクセスであるからだ。
static void Main(string[] args)
{
var pc = new PublicClass();
var ic = new InternalClass();
var dc = new DefaultClass();
}
Sub Main()
Dim pc = New PublicClass()
Dim ic = New InternalClass()
Dim dc = New DefaultClass()
End Sub
別のアセンブリであるProjectBプロジェクトのMainメソッドからは、前述した3つのクラスのうち、publicアクセス修飾子を付けたクラスにしかアクセスできない(次のコード)。
static void Main(string[] args)
{
var pc = new ProjectA.PublicClass();
//var ic = new ProjectA.InternalClass(); // コンパイルエラー
//var dc = new ProjectA.DefaultClass(); // コンパイルエラー
}
Sub Main()
Dim pc = New ProjectA.PublicClass()
'Dim ic = New ProjectA.InternalClass() ' コンパイルエラー
'Dim dc = New ProjectA.DefaultClass() ' コンパイルエラー
End Sub
トップレベルコンテナにはpublicアクセス修飾子が付いていない限りは別のアセンブリからアクセスできないと上で述べた。
基本はそうなのであるが、特定のアセンブリだけからはアクセスを許可したい場合がある。例えば、ユニットテストのプロジェクトからはアクセスさせたいといった場合だ。そのようなときには、アクセスされる側のアセンブリにInternalsVisibleTo属性(Runtime.CompilerServices名前空間)を追加する(次のコード)。
[assembly:InternalsVisibleTo("ProjectB")]
<Assembly: Runtime.CompilerServices.InternalsVisibleTo("ProjectB")>
メンバに対するアクセシビリティーは、6種類(C#)/5種類(VB)ある。
ただし、アクセス修飾子を付けないときの既定のアクセシビリティーは、コンテナの種類によってpublicかprivateのどちらかに決まっている。
コンテナの種類とそのメンバに対する既定のアクセシビリティーの一覧を次の表に示す。
コンテナ | そのメンバに対する既定のアクセシビリティー |
---|---|
クラス | private |
構造体 | private(C#)/Public(VB) |
列挙型 | public |
インタフェース | public |
さて、メンバにどのようなアクセス修飾子を付けられるかだが、まず列挙型とインタフェースのメンバには何も付けられない。上の表で示した既定のアクセシビリティーであるpublicになる。列挙型とインタフェースのメンバは常にpublicなのである。
残りのクラスと構造体のメンバには、public/private以外にも付けられるアクセス修飾子がある(次の表)。なお、クラスには全てのアクセス修飾子を付けられるが、構造体にはpublic/internal(C#)/Friend(VB)/privateアクセス修飾子の3種類しか付けられない。
アクセス修飾子 | 構造体での利用 | 意味 |
---|---|---|
public | 〇 | どこからでもアクセスできる(ただし、コンテナのアクセシビリティーに制限される) |
internal(C#)/ Friend(VB) |
〇 | アセンブリ外からはアクセスできない |
private | 〇 | そのコンテナ内からしかアクセスできない |
protected | × | privateに加えて、継承したクラスからもアクセスできる |
protected internal(C#)/ Protected Friend(VB) |
× | protectedに加えて、同じアセンブリからもアクセスできる。いわば「protected OR internal」 |
protected private(C# 7.2以降のみ) | × | protectedと同様だが、ただし、継承したクラスであっても別のアセンブリからはアクセスできない。いわば「protected AND internal」 |
クラスのメンバに付けられるアクセス修飾子の例を見てみよう。前述のProjectAプロジェクトのPublicClassクラスに、次のコードのようにメンバを追加する。
namespace ProjectA
{
public class PublicClass
{
public string PublicMember { get; set; }
internal string InternalMember { get; set; }
private string PrivateMember { get; set; }
protected string ProtectedMember { get; set; }
protected internal string ProtectedInternalMember { get; set; }
protected private string ProtectedPrivateMember { get; set; }
}
}
Public Class PublicClass
Public Property PublicMember As String
Friend Property InternalMember As String
Private PrivateMember As String
Protected Property ProtectedMember As String
Protected Friend Property ProtectedInternalMember As String
'Protected Private Property ProtectedPrivateMember As String ' VB 15では未サポート
End Class
上記のメンバに対して、同じクラスの中からは全てにアクセスできる(次のコード)。
namespace ProjectA
{
public class PublicClass
{
……省略……
public void PublicClassMethod()
{
this.PublicMember = "foo";
this.InternalMember = "foo";
this.PrivateMember = "foo";
this.ProtectedMember = "foo";
this.ProtectedInternalMember = "foo";
this.ProtectedPrivateMember = "foo";
}
}
}
Public Class PublicClass
……省略……
Public Sub PublicClassMethod()
Me.PublicMember = "foo"
Me.InternalMember = "foo"
Me.PrivateMember = "foo"
Me.ProtectedMember = "foo"
Me.ProtectedInternalMember = "foo"
End Sub
End Class
ProjectAプロジェクトに、PublicClassを継承したChildClassを追加し、その中からPublicClassのメンバにアクセスしてみよう(次のコード)。同じアセンブリ内で継承したクラスでは、このようにprivate以外のメンバにアクセスできる。
namespace ProjectA
{
class ChildClass : PublicClass
{
void ChildClassMethod()
{
base.PublicMember = "foo";
base.InternalMember = "foo";
//base.PrivateMember = "foo"; // コンパイルエラー
base.ProtectedMember = "foo";
base.ProtectedInternalMember = "foo";
base.ProtectedPrivateMember = "foo";
}
}
}
Public Class ChildClass
Inherits PublicClass
Public Sub ChildClassMethod()
MyBase.PublicMember = "foo"
MyBase.InternalMember = "foo"
'MyBase.PrivateMember = "foo" ' コンパイルエラー
MyBase.ProtectedMember = "foo"
MyBase.ProtectedInternalMember = "foo"
End Sub
End Class
同じアセンブリ内でも継承関係にないクラスからのアクセスでは、さらにprotectedメンバにもアクセスできなくなる(次のコード)。ただし、「protected internal(C#)/Protected Friend(VB)」アクセス修飾子には、internalとしてのアクセスが可能だ。
namespace ProjectA
{
class SameAssemblyClass
{
void SameAssemblyClassMethod()
{
var pc = new PublicClass();
pc.PublicMember = "foo";
pc.InternalMember = "foo";
//pc.PrivateMember = "foo"; // コンパイルエラー
//pc.ProtectedMember = "foo"; // コンパイルエラー
pc.ProtectedInternalMember = "foo"; // internalとしてのアクセス
//pc.ProtectedPrivateMember = "foo"; // コンパイルエラー
}
}
}
Public Class SameAssemblyClass
Public Sub SameAssemblyClassMethod()
Dim pc = New PublicClass()
pc.PublicMember = "foo"
pc.InternalMember = "foo"
'pc.PrivateMember = "foo" ' コンパイルエラー
'pc.ProtectedMember = "foo" ' コンパイルエラー
pc.ProtectedInternalMember = "foo" ' Friendとしてのアクセス
End Sub
End Class
では、別のアセンブリからのアクセスを試してみよう。まずは、継承関係にないAnotherAssemblyClassクラスをProjectBプロジェクトに作り、ここまでと同じようにProjectAプロジェクトのPublicクラスにアクセスしてみる(次のコード)。
namespace ProjectB
{
class AnotherAssemblyClass
{
void AnotherAssemblyClassMethod()
{
var pc = new ProjectA.PublicClass();
pc.PublicMember = "foo";
//pc.InternalMember = "foo"; // コンパイルエラー
//pc.PrivateMember = "foo"; // コンパイルエラー
//pc.ProtectedMember = "foo"; // コンパイルエラー
//pc.ProtectedInternalMember = "foo"; // コンパイルエラー
//pc.ProtectedPrivateMember = "foo"; // コンパイルエラー
}
}
}
Public Class AnotherAssemblyClass
Public Sub AnotherAssemblyClassMethod()
Dim pc = New ProjectA.PublicClass()
pc.PublicMember = "foo"
'pc.InternalMember = "foo" ' コンパイルエラー
'pc.PrivateMember = "foo" ' コンパイルエラー
'pc.ProtectedMember = "foo" ' コンパイルエラー
'pc.ProtectedInternalMember = "foo" ' コンパイルエラー
End Sub
End Class
最後に、別のアセンブリで継承したクラスの場合だ。ProjectAプロジェクトのPublicクラスを継承したAnotherAssemblyChildClassクラスを、ProjectBプロジェクトに追加する(次のコード)。前の例に比べて、protectedにもアクセスできるようになる。
namespace ProjectB
{
public class AnotherAssemblyChildClass : ProjectA.PublicClass
{
public void AnotherAssemblyChildClassMethod()
{
base.PublicMember = "foo";
//base.InternalMember = "foo"; // コンパイルエラー
//base.PrivateMember = "foo"; // コンパイルエラー
base.ProtectedMember = "foo";
base.ProtectedInternalMember = "foo";
//base.ProtectedPrivateMember = "foo"; // コンパイルエラー
}
}
}
Public Class AnotherAssemblyChildClass
Inherits ProjectA.PublicClass
Public Sub AnotherAssemblyChildClassMethod()
MyBase.PublicMember = "foo"
'MyBase.InternalMember = "foo" ' コンパイルエラー
'MyBase.PrivateMember = "foo" ' コンパイルエラー
MyBase.ProtectedMember = "foo"
MyBase.ProtectedInternalMember = "foo"
End Sub
End Class
本稿では、アクセス修飾子の種類とそれぞれのアクセシビリティーを解説した。実際にコーディングするに当たっては、アクセス修飾子なしで書き始めてから必要に応じてアクセシビリティーを広げていくという戦略を、慣れるまでは採用するとよいだろう。不必要に広いアクセシビリティーを与えてしまうというミスを防ぐことができる。
利用可能バージョン:.NET Framework 1.1以降(一部、C# 7.2以降)
カテゴリ:C# 処理対象:言語構文
カテゴリ:Visual Basic 処理対象:言語構文
関連TIPS:手軽にプロパティを実装するには?[C#、VS 2008、3.5]
【2018/09/25】本記事の一部に以下のような誤りがありました。おわびして訂正させていただくとともに、ご指摘いただいた読者の方に感謝いたします。
誤: クラスと構造体のメンバは、既定ではprivate(他からアクセスできない)。
正: C#:クラスと構造体のメンバは、既定ではprivate(他からアクセスできない)。
VB:クラスのメンバは、既定ではPrivate(他からアクセスできない)。構造体のメンバは、既定でPublic(他からもアクセス可能)。
【2018/01/24】初版公開。
Copyright© Digital Advantage Corp. All Rights Reserved.