DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

public、protected、internal、private、protected internal(Public、Protected、Friend、Private、Protected Friend)の違い

ここでは、public、protected、internal、private、protected internal(VB.NETではそれぞれ、Public、Protected、Friend、Private、Protected Friend)をクラスのメンバに使用した時どのような違いがあるのかについて説明します。

このことについて、MSDN(「アクセシビリティ レベル」、VB.NETでは「アクセシビリティ」)では次のように説明されています。

宣言されたアクセシビリティ 説明
public
(Public)
アクセスの制限はありません。
protected
(Protected)
アクセスは、コンテナ クラス、またはコンテナ クラスから派生した型に制限されます。
internal
(Friend)
アクセスは現在のアセンブリに制限されます。
protected internal
(Protected Friend)
アクセスは、現在のアセンブリ、またはコンテナ クラスから派生した型に制限されます。
private
(Private)
アクセスはコンテナ型に制限されます。

これだけでは何のことだか、さっぱり分かりません(分かる方はこれ以上お読みいただく必要はありません)。そこでここでは具体例を挙げてそれぞれの違いを説明します。

publicとprivateの違い

まずは、publicとprivateの違いを知っておきましょう。初心者の方はとりあえずこれだけ覚えておいてください。

VB.NET
コードを隠すコードを選択
Class Class1
    'Publicフィールド
    Public PublicValue As Integer
    'Privateフィールド
    Private PrivateValue As Integer

    'Publicメソッド
    Public Sub SetValue(ByVal val As Integer)
        'パブリックメンバにはもちろんアクセスできる
        PublicValue = val
        'プライベートメンバにも同じクラス内ではアクセスできる
        PrivateValue = val
    End Sub
End Class

Class MainClass

    Public Shared Sub Main()
        Dim c As New Class1

        '「...: 'Class1.PrivateValue' は
        ' アクセスできない保護レベルになっています。」
        'というエラーが出る
        c.PrivateValue = 0

        'Publicであれば問題なし
        c.PublicValue = 1

        c.SetValue(0)
    End Sub
End Class
C#
コードを隠すコードを選択
class Class1
{
    //publicフィールド
    public int PublicValue;
    //privateフィールド
    private int PrivateValue;

    //publicメソッド
    public void SetValue(int val)
    {
        //パブリックメンバにはもちろんアクセスできる
        PublicValue = val;
        //プライベートメンバにも同じクラス内ではアクセスできる
        PrivateValue = val;
    }
}

class MainClass
{
    public static void Main()
    {
        Class1 c = new Class1();

        //「...: 'Class1.PrivateValue' は
        // アクセスできない保護レベルになっています。」
        //というエラーが出る
        c.PrivateValue = 0;

        //publicであれば問題なし
        c.PublicValue = 1;

        c.SetValue(0);
    }
}

上記のコードでは、MainClassクラスから、外部のクラスClass1のフィールド(メンバ変数)に新しい値を代入しようとしています。この時、publicが指定されたフィールドPublicValue(パブリックメンバ、公開メンバなどと呼ぶ)への代入は問題なく行えますが、これに対してprivateが指定されたフィールドPrivateValue(プライベートメンバ、私的メンバなどと呼ぶ)への代入では、エラーが発生して失敗します(コンパイルで失敗します)。しかしこのPrivateValueへの代入も、宣言されているクラス内(Class1内)では問題なく行えます。

上記のMSDNでの説明をこのケースに当てはめると、

public (Public) : どのクラスからもアクセスできる。
private (Private) : そのメンバが宣言されているクラス内でアクセスできる。

となるでしょう。

結局その使い分けは、

どこからでも使いたいフィールド(メンバ変数)、メソッド、プロパティ、クラス、構造体などにはpublicを指定し、そのクラス内でしか使用しないものにはprivateを指定する

と覚えておけば、初心者の方はとりあえずOKでしょう。

すべての説明

ここからは、残りの説明です。まず、protectedが指定されたメンバには、そのメンバが宣言されているクラス内と、そのクラスから派生したクラス内からのみアクセスできます。つまりprotectedメンバのアクセス範囲は、
「private + 派生したクラス内」
となります。

internalが指定されたメンバには、そのメンバと同じアセンブリファイル(EXEファイルやDLLファイル)内からのみアクセスできます。

最後に、protected internalが指定されたメンバは、
「protected + internal」
のアクセス範囲を持ちます。

さてここでクラスライブラリ(DLL)とそれを使う実行ファイル(EXE)からなる簡単なプログラムを作成し、以上のアクセス修飾子のアクセス範囲を確認しましょう。まずは次のようなクラスライブラリを作成します。

VB.NET
コードを隠すコードを選択
Namespace ClassLibrary
    Public Class Class1
        'Publicフィールド
        Public PublicValue As Integer = 0
        'Privateフィールド
        Private PrivateValue As Integer = 0
        'Protectedフィールド
        Protected ProtectedValue As Integer = 0
        'Friendフィールド
        Friend FriendValue As Integer = 0
        'Protected Friendフィールド
        Protected Friend ProtectedFriendValue As Integer = 0

        '入れ子になったクラス
        Public Class Class11
            Public Sub Method1()
                Dim c As New Class1
                Dim i As Integer

                'OK
                i = c.PrivateValue
            End Sub
        End Class
    End Class

    Public Class Class2
        Public Sub Method1()
            Dim c As New Class1
            Dim i As Integer

            'OK
            i = c.PublicValue
            'エラー:アクセスできない保護レベル
            'i = c.PrivateValue
            'エラー:アクセスできない保護レベル
            'i = c.ProtectedValue
            'OK
            i = c.FriendValue
            'OK
            i = c.ProtectedFriendValue
        End Sub
    End Class

    'Class1からの派生クラス
    Public Class Class3
        Inherits Class1

        Public Sub Method1()
            Dim i As Integer

            'OK
            i = PublicValue
            'エラー:アクセスできない保護レベル
            'i = PrivateValue
            'OK
            i = ProtectedValue
            'OK
            i = FriendValue
            'OK
            i = ProtectedFriendValue
        End Sub
    End Class
End Namespace
C#
コードを隠すコードを選択
namespace ClassLibrary
{
    public class Class1
    {
        //publicフィールド
        public int PublicValue = 0;
        //privateフィールド
        private int PrivateValue = 0;
        //protectedフィールド
        protected int ProtectedValue = 0;
        //internalフィールド
        internal int InternalValue = 0;
        //protected internalフィールド
        protected internal int ProtectedInternalValue = 0;

        //入れ子になったクラス
        public class Class11
        {
            public void Method1()
            {
                Class1 c = new Class1();
                int i;

                //OK
                i = c.PrivateValue;
            }
        }
    }

    public class Class2
    {
        public void Method1()
        {
            Class1 c = new Class1();
            int i;

            //OK
            i = c.PublicValue;
            //エラー:アクセスできない保護レベル
            //i = c.PrivateValue;
            //エラー:アクセスできない保護レベル
            //i = c.ProtectedValue;
            //OK
            i = c.InternalValue;
            //OK
            i = c.ProtectedInternalValue;
        }
    }

    //Class1からの派生クラス
    public class Class3 : Class1
    {
        public void Method1()
        {
            int i;

            //OK
            i = PublicValue;
            //エラー:アクセスできない保護レベル
            //i = PrivateValue;
            //OK
            i = ProtectedValue;
            //OK
            i = InternalValue;
            //OK
            i = ProtectedInternalValue;
        }
    }
}

次にこのライブラリを使用する実行ファイルを作成します。

VB.NET
コードを隠すコードを選択
Namespace Console1
    Class MainClass
        Public Shared Sub Main()
            Dim c As New ClassLibrary.Class1
            Dim i As Integer

            'OK
            i = c.PublicValue
            'エラー:アクセスできない保護レベル
            'i = c.PrivateValue
            'エラー:アクセスできない保護レベル
            'i = c.ProtectedValue
            'エラー:アクセスできない保護レベル
            'i = c.FriendValue
            'エラー:アクセスできない保護レベル
            'i = c.ProtectedFriendValue
        End Sub
    End Class

    Class Class1
        Inherits ClassLibrary.Class1

        Public Sub Method1()
            Dim i As Integer

            'OK
            i = PublicValue
            'エラー:アクセスできない保護レベル
            'i = PrivateValue
            'OK
            i = ProtectedValue
            'エラー:アクセスできない保護レベル
            'i = FriendValue
            'OK
            i = ProtectedFriendValue
        End Sub
    End Class
End Namespace
C#
コードを隠すコードを選択
namespace Console1
{
    class MainClass
    {
        public static void Main()
        {
            ClassLibrary.Class1 c = new ClassLibrary.Class1();
            int i;

            //OK
            i = c.PublicValue;
            //エラー:アクセスできない保護レベル
            //i = c.PrivateValue;
            //エラー:アクセスできない保護レベル
            //i = c.ProtectedValue;
            //エラー:アクセスできない保護レベル
            //i = c.InternalValue;
            //エラー:アクセスできない保護レベル
            //i = c.ProtectedInternalValue;
        }
    }

    class Class1 : ClassLibrary.Class1
    {
        public void Method1()
        {
            int i;

            //OK
            i = PublicValue;
            //エラー:アクセスできない保護レベル
            //i = PrivateValue;
            //OK
            i = ProtectedValue;
            //エラー:アクセスできない保護レベル
            //i = InternalValue;
            //OK
            i = ProtectedInternalValue;
        }
    }
}

上記のコードでは、アクセスできず、エラーの発生する行のコードはコメントアウトされています。このコードをご覧いただければ、それぞれのアクセス範囲は一目瞭然でしょう。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • .NET Tipsをご利用いただく際は、注意事項をお守りください。