おはようございます。最近 C#を使った開発が増えてきましたが、今回は protected と internal について調べました。
C#を使って開発をして日が浅い方の参考にもなれば幸いです。
環境
OS:Windows11
エディタ:Visual Studio2022
フレームワーク:.NET6
プロジェクト名:WPF_Test_App
protected
protected は自分のクラス(基底クラス)と派生クラスからのみ参照することができます。派生クラスは継承先のクラスのことです。
実際にどのような動きになるのかコードで見ていきます。
using System;
using System.Diagnostics;
using System.Windows;
namespace WPF_Test_App
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Test();
}
public void Test()
{
var b = new B();
b.Main();
}
}
// 基底クラス
class A
{
// 派生クラスでも利用できるフィールド
protected string say = "Hello World";
public void Say()
{
Console.WriteLine(say);
}
protected void ProtectedSay()
{
Console.WriteLine(say);
}
private void PrivateSay()
{
Console.WriteLine(say);
}
protected internal void ProtectedInternalSay()
{
Console.WriteLine(say);
}
internal void InternalSay()
{
Console.WriteLine(say);
}
}
// 派生クラス(クラスAを継承した継承先のクラス)
class B : A
{
public void Main()
{
var a = new A(); // 基底クラスをインスタンス化
var b = new B(); // 派生クラスをインスタンス化
// A型の修飾子をとおしてプロテクトメンバーA.sayにアクセスすることはできません。
// 修飾子はB型、またはそれから派生したものでなければなりません
// 基底クラスでprotectedのものは、異なるクラスからのアクセスはprivateと同等となるのでエラーになる
//a.say = "World"; // Error CS1540
// publicは派生クラスからでもアクセスできる
// 基底クラスのメソッド経由でフィールドにアクセスしている
a.Say(); // Hello World
// baseキーワードで派生クラスから基底クラス内のメソッドなど にアクセス出来る
base.ProtectedSay();
base.InternalSay();
base.ProtectedInternalSay();
// privateはアクセスできない
//base.PrivateSay();
Debug.WriteLine(b.say); // Hello World
// 基底クラスのフィールドを書き換える
b.say = "World";
Debug.WriteLine(b.say); // World
}
}
}
70 行目にa.say = "World"; // Error CS1540
とありますが、Visual Studio では以下のようなエラーメッセージが出力されています。
A 型の修飾子をとおしてプロテクトメンバー A.say にアクセスすることはできません。修飾子は B 型、またはそれから派生したものでなければなりません
これは、基底クラスを直接インスタンス化して protected のフィールドにアクセスしている為に発生しています。このような使い方をするのは特殊だと思いますが、今回は修飾子の動作確認として使用しています。
基底クラスで protected のものは、異なるクラスからのアクセスは private と同等となるのでエラーになります。
internal
これは、同一のプログラム(アセンブリ)内からのアクセスを許可します。アセンブリとは exe ファイルや dll ファイルのことを指しますが、Visual Studio 内では同じプロジェクト内という意味になります。(別のプロジェクトからはアクセスできない)
先程のコードでクラス A と B にはアクセス修飾子がついていませんが、何もつけない場合は internal が付与されているものとみなされます。
因みに Visual Studio2022 から、プロジェクトに新しくクラスを作成した場合はデフォルトで internal が付与されています。
namespace WPF_Test_App
{
internal class Class1
{
}
// internal が付与されているのと同じ
class Class2
{
}
}
異なるプロジェクトを作成する
実際に異なるプロジェクトを作成して動きを確認していきます。
ソリューションエクスプローラーを右クリックして、「追加」「新しいプロジェクト」と進みます。
その後、適当なプロジェクト名を指定して新しく作成します。今回プロジェクト名は WPFInternal としました。
次に、プロジェクトに WPFInternalClass1 クラスを追加します。
クラスのアクセス修飾子がデフォルトで internal になっていると思いますので、一旦 public に変更しておきます。
namespace WPFInternal
{
public class WPFInternalClass1
{
internal string hello = "world";
}
}
それでは WPF_Test_App から参照できるように設定します。
WPF_Test_App の「依存関係」を右クリック、「プロジェクト参照の追加」を選択
参照に追加するプロジェクトにチェックをいてれ「OK ボタン」をクリック
これで WPF_Test_App から WPFInternal プロジェクトを参照できるようになりました。
WPF_Test_App の MainWindow.xaml.cs に WPFInternalClass1 を参照するような処理を追加します。
amespace WPF_Test_App
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void Test1()
{
var c = new Class1();
MessageBox.Show(c.hoge);
}
public void Test2()
{
var c = new WPFInternal.WPFInternalClass1();
MessageBox.Show(c.hello); // エラーコード CS0122
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//Test1();
Test2();
}
}
}
アクセスできない保護レベルであると出力されてエラーになります。
アクセス修飾子を指定しないと、保護レベルが internal になると述べましたが WPFInternalClass1 を以下のように修正して確認してみましょう。
namespace WPFInternal
{
class WPFInternalClass1
{
internal string hello = "world";
}
}
すると、インスタンス化する行のコードでもエラーが発生していることがわかります。
public void Test2()
{
// WPFInternalClass1 クラスの public を外すと エラー コード CS0122 が発生する
//var c = new WPFInternal.WPFInternalClass1();
//MessageBox.Show(c.hello); // エラーコード CS0122
}
最後に
今回は protected と internal に絞って見ていきましたが、サンプルコードをちょっと書き換えたり色々なパターンを試すと理解が深まると思います。
今回動作確認に使用したプロジェクトは Github に上げていますので、ちょっと動きを確認したいなという場面で使用してみてください。