C#のスコープとは?変数・メソッド・クラスで迷わない有効範囲を初心者向けに解説

はじめに

C#を学び始めたときに、意外とつまずきやすいのが「スコープ」です。

スコープとは、簡単にいうと「変数やメソッド、クラスなどを使える範囲」のことです。C#では、変数を宣言した場所によって、その変数を使える場所と使えない場所がはっきり決まります。

たとえば、if文の中で作った変数をif文の外で使おうとすると、エラーになることがあります。また、メソッドの中で宣言した変数を別のメソッドから直接使うこともできません。

この記事では、C#のスコープについて、変数・メソッド・クラス・フィールドの観点から初心者向けにわかりやすく解説します。エラー例やサンプルコードも紹介するので、「現在のコンテキストに名前が存在しません」といったエラーで悩んでいる方も、ぜひ参考にしてください。

1. C#のスコープとは?まずは「使える範囲」と考えよう

1-1. スコープとは変数・メソッド・クラスなどの名前が有効な範囲

C#のスコープとは、変数・メソッド・クラス・フィールドなどの「名前」が有効になる範囲のことです。

たとえば、次のコードを見てください。

C#
void Sample()
{
int number = 10;
Console.WriteLine(number);
}

このnumberという変数は、Sampleメソッドの中で宣言されています。そのため、基本的にはSampleメソッドの中でしか使えません。

C#
void Sample()
{
int number = 10;
}

void Other()
{
Console.WriteLine(number); // エラー
}

Otherメソッドの中ではnumberが宣言されていないため、C#は「numberという名前はここでは使えない」と判断します。

このように、スコープを理解することで「どこで宣言したものを、どこから使えるのか」がわかるようになります。

1-2. スコープを理解しないと起きる代表的なエラー

C#でスコープを理解していないと、次のようなエラーがよく発生します。

C#
if (true)
{
int score = 80;
}

Console.WriteLine(score); // エラー

このコードでは、scoreはif文の中で宣言されています。そのため、if文の外ではscoreを使えません。

よく見るエラーメッセージとしては、次のようなものがあります。

現在のコンテキストに 'score' という名前は存在しません

これは、「今書いている場所からは、その名前を参照できません」という意味です。

初心者のうちは、このエラーを見ると難しく感じるかもしれません。しかし、多くの場合は「変数を宣言した場所」と「変数を使おうとしている場所」を確認すれば原因が見つかります。

1-3. C#では基本的に波括弧 { } がスコープの目印になる

C#では、波括弧{ }で囲まれた範囲がスコープの目印になることが多いです。

C#
void Sample()
{
int x = 10;

if (x > 5)
{
int y = 20;
Console.WriteLine(y);
}

Console.WriteLine(x);
// Console.WriteLine(y); // エラー
}

この例では、xSampleメソッドのブロック内で宣言されているため、メソッド内で使えます。

一方、yはif文のブロック内で宣言されているため、if文の中でしか使えません。if文の外に出ると、yはスコープ外になります。

つまり、C#のスコープを考えるときは、まず波括弧{ }の範囲を見ることが大切です。

1-4. 「スコープ」と「アクセス修飾子」の違いを先に押さえる

C#を学んでいると、「スコープ」と「アクセス修飾子」が混ざってしまうことがあります。

スコープは、名前が有効な範囲のことです。

一方、アクセス修飾子は、クラスやメソッド、フィールドなどに対して「どこからアクセスできるか」を決める仕組みです。

代表的なアクセス修飾子には、次のようなものがあります。

C#
public
private
protected
internal

たとえば、privateを付けたメソッドは、同じクラスの中からしか呼び出せません。

C#
class Sample
{
private void Hello()
{
Console.WriteLine("Hello");
}
}

スコープは「名前が見える範囲」、アクセス修飾子は「外部から使えるかどうかの制限」と考えると整理しやすいです。

2. C#における変数のスコープ

2-1. ローカル変数のスコープは宣言したブロック内

C#でメソッドの中に宣言する変数を、ローカル変数と呼びます。

C#
void ShowMessage()
{
string message = "こんにちは";
Console.WriteLine(message);
}

このmessageはローカル変数です。ShowMessageメソッドの中で宣言されているため、ShowMessageメソッドの中でのみ使えます。

C#
void ShowMessage()
{
string message = "こんにちは";
}

void Print()
{
Console.WriteLine(message); // エラー
}

別のメソッドであるPrintからは、messageを直接使うことはできません。

ローカル変数のスコープは、基本的に「宣言したブロックの中」です。ブロックとは、波括弧{ }で囲まれた範囲のことです。

2-2. 変数は宣言した行より前では使えない

C#では、変数は宣言した後でなければ使えません。

C#
void Sample()
{
Console.WriteLine(number); // エラー
int number = 10;
}

このコードでは、numberを宣言する前に使おうとしているためエラーになります。

正しくは、次のように先に宣言してから使います。

C#
void Sample()
{
int number = 10;
Console.WriteLine(number);
}

同じブロック内であっても、宣言した行より前ではその変数を使えない点に注意しましょう。

2-3. if文・for文・while文の中で宣言した変数の有効範囲

if文の中で宣言した変数は、if文のブロック内でのみ使えます。

C#
void CheckScore(int score)
{
if (score >= 80)
{
string result = "合格";
Console.WriteLine(result);
}

// Console.WriteLine(result); // エラー
}

resultはif文の中で宣言されているため、if文の外では使えません。

for文の場合も同じです。

C#
for (int i = 0; i < 3; i++)
{
Console.WriteLine(i);
}

// Console.WriteLine(i); // エラー

iはfor文の中で宣言されているため、for文の外では使えません。

while文の中で宣言した変数も、そのwhile文のブロック内でのみ有効です。

C#
int count = 0;

while (count < 3)
{
string text = "処理中";
Console.WriteLine(text);
count++;
}

// Console.WriteLine(text); // エラー

制御文の中で宣言した変数は、そのブロックを出ると使えなくなると覚えておきましょう。

2-4. メソッド内で宣言した変数は他のメソッドから使えない

メソッド内で宣言したローカル変数は、そのメソッド専用の変数です。

C#
class Program
{
void MethodA()
{
int value = 100;
}

void MethodB()
{
Console.WriteLine(value); // エラー
}
}

valueMethodAの中で宣言されているため、MethodBからは見えません。

複数のメソッドで同じデータを使いたい場合は、フィールドを使う方法があります。

C#
class Program
{
int value = 100;

void MethodA()
{
Console.WriteLine(value);
}

void MethodB()
{
Console.WriteLine(value);
}
}

この例では、valueはクラスの中に宣言されたフィールドです。フィールドは、同じクラス内の複数のメソッドから使えます。

2-5. 同じ名前の変数を宣言するときの注意点

C#では、同じスコープ内に同じ名前のローカル変数を宣言することはできません。

C#
void Sample()
{
int number = 10;
int number = 20; // エラー
}

同じブロックの中でnumberを2回宣言しているため、エラーになります。

また、内側のブロックで同じ名前を使おうとしても、C#ではエラーになるケースがあります。

C#
void Sample()
{
int number = 10;

if (true)
{
int number = 20; // エラー
}
}

読みやすさのためにも、同じ名前の変数を何度も使うのは避けたほうがよいです。

変数名は、役割がわかる名前にしましょう。

C#
int totalScore = 100;
int passingScore = 80;

このように書くと、コードを読んだときに意味が伝わりやすくなります。

3. メソッドのスコープと呼び出せる範囲

3-1. メソッドはクラスの中に定義する

C#では、基本的にメソッドはクラスの中に定義します。

C#
class Calculator
{
int Add(int a, int b)
{
return a + b;
}
}

この例では、AddメソッドはCalculatorクラスの中にあります。

メソッドは変数とは違い、ブロックの中で自由に定義するものではありません。通常はクラスや構造体などの型のメンバーとして定義します。

3-2. 同じクラス内のメソッドを呼び出す基本

同じクラス内にあるメソッドは、基本的にメソッド名を書くだけで呼び出せます。

C#
class Program
{
void SayHello()
{
Console.WriteLine("こんにちは");
}

void Run()
{
SayHello();
}
}

Runメソッドの中から、同じクラス内のSayHelloメソッドを呼び出しています。

ただし、staticが関係する場合は注意が必要です。staticメソッドからインスタンスメソッドを直接呼ぶことはできません。

C#
class Program
{
void SayHello()
{
Console.WriteLine("こんにちは");
}

static void Main()
{
// SayHello(); // エラー
}
}

この場合は、インスタンスを作成してから呼び出します。

C#
class Program
{
void SayHello()
{
Console.WriteLine("こんにちは");
}

static void Main()
{
Program program = new Program();
program.SayHello();
}
}

3-3. 他のクラスのメソッドを呼び出すにはインスタンスが必要な場合がある

別のクラスにあるメソッドを呼び出す場合、そのメソッドがインスタンスメソッドであれば、基本的にはインスタンスを作成する必要があります。

C#
class Printer
{
public void PrintMessage()
{
Console.WriteLine("メッセージを表示します");
}
}

class Program
{
static void Main()
{
Printer printer = new Printer();
printer.PrintMessage();
}
}

PrintMessagePrinterクラスのインスタンスメソッドです。そのため、new Printer()でインスタンスを作成し、printer.PrintMessage()のように呼び出します。

一方、staticメソッドであれば、クラス名から直接呼び出せます。

C#
class Printer
{
public static void PrintMessage()
{
Console.WriteLine("メッセージを表示します");
}
}

class Program
{
static void Main()
{
Printer.PrintMessage();
}
}

この違いは、C#のスコープや呼び出し範囲を理解するうえで重要です。

3-4. staticメソッドとインスタンスメソッドのスコープの違い

staticメソッドは、クラスそのものに属するメソッドです。インスタンスを作らなくても呼び出せます。

C#
class MathHelper
{
public static int Double(int number)
{
return number * 2;
}
}

class Program
{
static void Main()
{
int result = MathHelper.Double(5);
Console.WriteLine(result);
}
}

一方、インスタンスメソッドは、作成したオブジェクトに属します。

C#
class Counter
{
private int count = 0;

public void Increment()
{
count++;
}

public int GetCount()
{
return count;
}
}

この場合、countは各インスタンスごとに保持されます。

C#
Counter counter1 = new Counter();
Counter counter2 = new Counter();

counter1.Increment();

Console.WriteLine(counter1.GetCount()); // 1
Console.WriteLine(counter2.GetCount()); // 0

staticはクラス全体で共有され、インスタンスメンバーはオブジェクトごとに分かれる、と考えると理解しやすいです。

3-5. public・privateでメソッドの見える範囲が変わる

メソッドには、publicprivateなどのアクセス修飾子を付けられます。

C#
class Sample
{
public void PublicMethod()
{
Console.WriteLine("外部から呼び出せます");
}

private void PrivateMethod()
{
Console.WriteLine("同じクラス内からのみ呼び出せます");
}
}

publicメソッドは、クラスの外から呼び出せます。

C#
Sample sample = new Sample();
sample.PublicMethod();

一方、privateメソッドはクラスの外から呼び出せません。

C#
Sample sample = new Sample();
// sample.PrivateMethod(); // エラー

privateメソッドは、同じクラス内の処理を分けるためによく使われます。

C#
class Sample
{
public void Run()
{
Prepare();
Execute();
}

private void Prepare()
{
Console.WriteLine("準備");
}

private void Execute()
{
Console.WriteLine("実行");
}
}

外部に公開する必要がないメソッドは、基本的にprivateにしておくと安全です。

4. クラス・フィールドのスコープ

4-1. クラス内で宣言するフィールドとは

フィールドとは、クラスの中に直接宣言する変数のことです。

C#
class Player
{
private int hp = 100;
}

このhpはフィールドです。メソッドの中で宣言するローカル変数とは違い、クラスのメンバーとして扱われます。

フィールドは、オブジェクトの状態を保持するためによく使われます。

C#
class Player
{
private string name;
private int hp;

public Player(string playerName)
{
name = playerName;
hp = 100;
}
}

この例では、プレイヤーの名前や体力をフィールドとして保持しています。

4-2. フィールドは同じクラス内の複数メソッドから使える

フィールドは、同じクラス内の複数のメソッドから使えます。

C#
class Player
{
private int hp = 100;

public void Damage(int amount)
{
hp -= amount;
}

public void ShowHp()
{
Console.WriteLine(hp);
}
}

hpDamageメソッドでもShowHpメソッドでも使えます。

C#
Player player = new Player();
player.Damage(30);
player.ShowHp(); // 70

複数のメソッドで共通して使いたい値は、フィールドとして持たせると便利です。

ただし、何でもフィールドにすればよいわけではありません。一時的な計算にしか使わない値は、ローカル変数にしたほうが安全です。

4-3. ローカル変数とフィールドの違い

ローカル変数とフィールドの大きな違いは、宣言する場所と使える範囲です。

ローカル変数は、メソッドやブロックの中で宣言します。

C#
void Sample()
{
int number = 10;
}

このnumberは、Sampleメソッドの中でしか使えません。

一方、フィールドはクラスの中に直接宣言します。

C#
class Sample
{
private int number = 10;

void MethodA()
{
Console.WriteLine(number);
}

void MethodB()
{
Console.WriteLine(number);
}
}

このnumberはフィールドなので、同じクラス内の複数のメソッドから使えます。

ローカル変数は一時的な値、フィールドはオブジェクトの状態を保持する値、と考えると使い分けやすくなります。

4-4. thisを使ってフィールドとローカル変数を区別する

フィールドとローカル変数の名前が同じ場合、thisを使ってフィールドを明示できます。

C#
class Player
{
private string name;

public Player(string name)
{
this.name = name;
}
}

このコードでは、コンストラクターの引数nameとフィールドnameが同じ名前です。

C#
this.name = name;

左側のthis.nameはフィールドを表し、右側のnameは引数を表します。

thisは「このインスタンス自身」を意味します。フィールドとローカル変数や引数を区別したいときに使います。

ただし、名前の重複が多すぎるとコードが読みづらくなることもあります。必要に応じて、わかりやすい名前を付けることも大切です。

4-5. staticフィールドはクラス全体で共有される

staticフィールドは、インスタンスごとではなくクラス全体で共有されるフィールドです。

C#
class Counter
{
public static int TotalCount = 0;

public Counter()
{
TotalCount++;
}
}

この例では、Counterのインスタンスが作られるたびにTotalCountが増えます。

C#
Counter c1 = new Counter();
Counter c2 = new Counter();

Console.WriteLine(Counter.TotalCount); // 2

TotalCountは各インスタンスに別々に存在するのではなく、Counterクラス全体で1つだけ存在します。

一方、通常のフィールドはインスタンスごとに存在します。

C#
class Player
{
public int Hp = 100;
}
C#
Player player1 = new Player();
Player player2 = new Player();

player1.Hp = 50;

Console.WriteLine(player1.Hp); // 50
Console.WriteLine(player2.Hp); // 100

staticフィールドは便利ですが、どこからでも共有される値になるため、使いすぎると状態の管理が難しくなることがあります。

5. C#初心者がつまずきやすいスコープのエラー例

5-1. 「現在のコンテキストに名前が存在しません」が出る原因

C#でよくあるエラーに、次のようなものがあります。

現在のコンテキストに 'xxx' という名前は存在しません

これは、今書いている場所からxxxという名前を参照できないという意味です。

主な原因は次のとおりです。

変数を宣言していない、宣言したブロックの外で使っている、宣言する前に使っている、アクセス修飾子によって外部から見えない、名前を打ち間違えている、といったケースです。

たとえば、次のコードはエラーになります。

C#
void Sample()
{
int age = 20;
}

void Print()
{
Console.WriteLine(age); // エラー
}

ageSampleメソッドのローカル変数なので、Printメソッドからは使えません。

5-2. if文の中で作った変数を外で使おうとしている

if文の中で作った変数は、if文の外では使えません。

C#
void Sample(bool isMember)
{
if (isMember)
{
int discount = 500;
}

Console.WriteLine(discount); // エラー
}

discountはif文の中で宣言されているため、if文の外ではスコープ外です。

外でも使いたい場合は、if文の外で先に宣言します。

C#
void Sample(bool isMember)
{
int discount = 0;

if (isMember)
{
discount = 500;
}

Console.WriteLine(discount);
}

このように、使いたい範囲に合わせて変数を宣言する場所を決めましょう。

5-3. for文のカウンター変数をループ外で使おうとしている

for文でよく使うカウンター変数も、スコープに注意が必要です。

C#
for (int i = 0; i < 5; i++)
{
Console.WriteLine(i);
}

Console.WriteLine(i); // エラー

iはfor文の中で宣言されているため、for文の外では使えません。

ループ後にも値を使いたい場合は、for文の外で変数を宣言します。

C#
int i;

for (i = 0; i < 5; i++)
{
Console.WriteLine(i);
}

Console.WriteLine(i);

ただし、ループカウンターは基本的にループ内だけで使うことが多いです。特別な理由がなければ、for文の中で宣言するほうがスコープを狭くでき、コードが読みやすくなります。

5-4. メソッド内の変数を別メソッドから使おうとしている

初心者がよくやってしまうのが、あるメソッド内で宣言した変数を別のメソッドから使おうとするケースです。

C#
class Sample
{
void Input()
{
string name = "田中";
}

void Output()
{
Console.WriteLine(name); // エラー
}
}

nameInputメソッドのローカル変数なので、Outputメソッドからは使えません。

複数のメソッドで共有したい場合は、フィールドにします。

C#
class Sample
{
private string name;

void Input()
{
name = "田中";
}

void Output()
{
Console.WriteLine(name);
}
}

ただし、値を受け渡すだけでよい場合は、引数や戻り値を使うほうが適切なこともあります。

C#
class Sample
{
string CreateName()
{
return "田中";
}

void Output()
{
string name = CreateName();
Console.WriteLine(name);
}
}

何でもフィールドにするのではなく、「本当にクラスの状態として持つ必要があるか」を考えることが大切です。

5-5. privateメンバーをクラス外から呼び出そうとしている

privateが付いたメンバーは、同じクラス内からしか使えません。

C#
class User
{
private string name = "山田";
}

このnameをクラスの外から直接使おうとするとエラーになります。

C#
User user = new User();
// Console.WriteLine(user.name); // エラー

外部から値を取得したい場合は、メソッドやプロパティを用意します。

C#
class User
{
private string name = "山田";

public string GetName()
{
return name;
}
}
C#
User user = new User();
Console.WriteLine(user.GetName());

C#では、フィールドを直接公開するよりも、privateにして必要な操作だけpublicメソッドやプロパティで公開する書き方がよく使われます。

6. スコープを理解するためのサンプルコード

6-1. ローカル変数のスコープを確認するコード

次のコードでは、ローカル変数がメソッド内でのみ使えることを確認できます。

C#
using System;

class Program
{
static void Main()
{
int number = 10;
Console.WriteLine(number);
}
}

numberMainメソッドの中で宣言されています。そのため、Mainメソッドの中では使えます。

別のメソッドから使おうとするとエラーになります。

C#
using System;

class Program
{
static void Main()
{
int number = 10;
Console.WriteLine(number);
}

static void Print()
{
// Console.WriteLine(number); // エラー
}
}

ローカル変数は、宣言したメソッドやブロックの中だけで使えると覚えておきましょう。

6-2. if文・for文で変数が使える範囲を確認するコード

if文の中で宣言した変数は、if文の外では使えません。

C#
using System;

class Program
{
static void Main()
{
int score = 85;

if (score >= 80)
{
string message = "合格です";
Console.WriteLine(message);
}

// Console.WriteLine(message); // エラー
}
}

for文のカウンター変数も、for文の外では使えません。

C#
using System;

class Program
{
static void Main()
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine(i);
}

// Console.WriteLine(i); // エラー
}
}

このように、if文やfor文の中で宣言した変数は、そのブロック内でのみ使えます。

6-3. フィールドとローカル変数の違いがわかるコード

次のコードでは、フィールドとローカル変数の違いを確認できます。

C#
using System;

class Player
{
private int hp = 100;

public void Damage(int amount)
{
int newHp = hp - amount;
hp = newHp;
}

public void ShowHp()
{
Console.WriteLine(hp);
// Console.WriteLine(newHp); // エラー
}
}

hpはフィールドなので、DamageメソッドとShowHpメソッドの両方から使えます。

一方、newHpDamageメソッド内のローカル変数です。そのため、ShowHpメソッドからは使えません。

C#
class Program
{
static void Main()
{
Player player = new Player();
player.Damage(30);
player.ShowHp(); // 70
}
}

フィールドは複数のメソッドで共有でき、ローカル変数は一時的な処理に使うものです。

6-4. staticとインスタンスの違いがわかるコード

次のコードでは、staticフィールドとインスタンスフィールドの違いを確認できます。

C#
using System;

class Counter
{
public static int TotalCount = 0;
public int Count = 0;

public void Increment()
{
TotalCount++;
Count++;
}
}

class Program
{
static void Main()
{
Counter counter1 = new Counter();
Counter counter2 = new Counter();

counter1.Increment();
counter1.Increment();
counter2.Increment();

Console.WriteLine(Counter.TotalCount); // 3
Console.WriteLine(counter1.Count); // 2
Console.WriteLine(counter2.Count); // 1
}
}

TotalCountstaticフィールドなので、クラス全体で共有されます。

一方、Countはインスタンスフィールドなので、counter1counter2で別々の値を持ちます。

staticは共有、インスタンスは個別、と考えるとわかりやすいです。

6-5. エラーになるコードと修正後のコードを比較する

まずは、エラーになるコードです。

C#
using System;

class Program
{
static void Main()
{
if (true)
{
string message = "こんにちは";
}

Console.WriteLine(message); // エラー
}
}

messageはif文の中で宣言されているため、if文の外では使えません。

修正するには、if文の外で変数を宣言します。

C#
using System;

class Program
{
static void Main()
{
string message = "";

if (true)
{
message = "こんにちは";
}

Console.WriteLine(message);
}
}

このように、変数をどこで使いたいのかを考えて、宣言する場所を決めることが大切です。

7. スコープを適切に使うための考え方

7-1. 変数は必要な範囲でだけ宣言する

C#では、変数のスコープをできるだけ狭くすることが大切です。

たとえば、次のようにメソッドの最初にすべての変数を宣言する必要はありません。

C#
void Sample()
{
int total;
int price;
int count;

price = 100;
count = 3;
total = price * count;

Console.WriteLine(total);
}

現在のC#では、必要になった場所で変数を宣言するほうが読みやすいです。

C#
void Sample()
{
int price = 100;
int count = 3;
int total = price * count;

Console.WriteLine(total);
}

さらに、if文の中でしか使わない変数なら、if文の中で宣言します。

C#
if (score >= 80)
{
string message = "合格";
Console.WriteLine(message);
}

変数を使う範囲が狭いほど、コードの影響範囲も小さくなります。

7-2. 何でもフィールドにするとバグの原因になる

複数のメソッドで使えるからといって、何でもフィールドにするのは避けましょう。

C#
class Calculator
{
private int result;

public void Add(int a, int b)
{
result = a + b;
}

public void Print()
{
Console.WriteLine(result);
}
}

このような書き方が必要な場合もありますが、一時的な計算結果であればローカル変数で十分です。

C#
class Calculator
{
public void AddAndPrint(int a, int b)
{
int result = a + b;
Console.WriteLine(result);
}
}

フィールドはクラスの状態として残るため、意図しないタイミングで値が変わったり、前回の値が残ったりすることがあります。

一時的な値はローカル変数にし、オブジェクトの状態として必要な値だけフィールドにしましょう。

7-3. 変数名の重複を避けて読みやすくする

同じような名前の変数が多いと、どの値を使っているのかわかりにくくなります。

C#
int value = 10;

if (true)
{
int value2 = 20;
}

この程度ならまだ読めますが、実際のコードでdatadata2temptemp2のような名前が増えると混乱しやすくなります。

変数名は、役割がわかる名前にしましょう。

C#
int itemPrice = 1000;
int taxAmount = 100;
int totalPrice = itemPrice + taxAmount;

スコープを狭くし、変数名を具体的にすることで、コードの見通しがよくなります。

7-4. privateを基本にして必要なものだけpublicにする

クラスのメンバーは、基本的にprivateにしておくのがおすすめです。

C#
class User
{
private string name;

public void SetName(string value)
{
name = value;
}

public string GetName()
{
return name;
}
}

外部から直接触らせる必要があるものだけ、publicにします。

C#
class User
{
public string Name { get; private set; }

public User(string name)
{
Name = name;
}
}

何でもpublicにすると、クラスの外から自由に変更できてしまい、バグの原因になります。

スコープやアクセス範囲は、広げるよりも狭く保つほうが安全です。

7-5. スコープを狭くするとコードの見通しがよくなる

スコープを狭くすると、その変数がどこで使われているのかを追いやすくなります。

C#
void Sample()
{
if (DateTime.Now.Hour < 12)
{
string greeting = "おはようございます";
Console.WriteLine(greeting);
}
}

このコードでは、greetingはif文の中だけで使われています。外から影響を受けることもなく、外に影響を与えることもありません。

一方、フィールドや広いスコープの変数が多いと、「どこで変更されたのか」を探すのが大変になります。

スコープを狭くすることは、バグを減らし、コードを読みやすくする基本です。

8. C#のスコープに関するよくある質問

8-1. スコープと寿命は同じ意味?

スコープと寿命は似ていますが、厳密には同じ意味ではありません。

スコープは「名前を使える範囲」です。

一方、寿命は「値やオブジェクトがメモリ上に存在している期間」です。

たとえば、ローカル変数はスコープを抜けると名前としては使えなくなります。ただし、参照型のオブジェクトの場合、そのオブジェクト自体がすぐに消えるとは限りません。どこかから参照されていれば、まだ存在し続けることがあります。

初心者のうちは、まず「スコープは名前が使える範囲」と覚えれば十分です。

8-2. ローカル変数とメンバ変数の違いは?

ローカル変数は、メソッドやブロックの中で宣言する変数です。

C#
void Sample()
{
int number = 10;
}

このnumberはローカル変数です。Sampleメソッドの中でしか使えません。

メンバ変数という言葉は、一般的にはクラスのメンバーとして宣言されたフィールドを指すことが多いです。

C#
class Sample
{
private int number = 10;
}

このnumberはフィールドです。同じクラス内の複数のメソッドから使えます。

簡単にいうと、ローカル変数は一時的な値、フィールドはオブジェクトの状態を保持する値です。

8-3. クラス外から変数を使うにはどうすればいい?

クラス外から値を使いたい場合は、フィールドを直接公開するのではなく、プロパティやメソッドを使うのが一般的です。

C#
class User
{
private string name;

public string Name
{
get { return name; }
set { name = value; }
}
}

または、自動実装プロパティを使うと簡潔に書けます。

C#
class User
{
public string Name { get; set; }
}

使う側は次のように書けます。

C#
User user = new User();
user.Name = "佐藤";
Console.WriteLine(user.Name);

ただし、外部から自由に変更させたくない場合は、setprivateにすることもできます。

C#
class User
{
public string Name { get; private set; }

public User(string name)
{
Name = name;
}
}

クラス外から使う必要があるものだけ、適切に公開しましょう。

8-4. staticを付けるとスコープはどう変わる?

staticを付けると、インスタンスではなくクラスに属するメンバーになります。

C#
class Sample
{
public static int Count = 0;
}

この場合、Countはインスタンスを作らなくてもクラス名から呼び出せます。

C#
Sample.Count++;
Console.WriteLine(Sample.Count);

ただし、staticを付けたからといって、どこからでも無条件に使えるわけではありません。private staticであれば、同じクラス内からしか使えません。

C#
class Sample
{
private static int count = 0;
}

つまり、staticは「クラスに属するか、インスタンスに属するか」を決めるものであり、外部から見える範囲はpublicprivateなどのアクセス修飾子によって決まります。

8-5. UnityのC#でもスコープの考え方は同じ?

Unityで使うC#でも、スコープの基本的な考え方は同じです。

C#
using UnityEngine;

public class Player : MonoBehaviour
{
private int hp = 100;

void Start()
{
int startValue = 10;
Debug.Log(startValue);
}

void Update()
{
// Debug.Log(startValue); // エラー
Debug.Log(hp);
}
}

startValueStartメソッド内のローカル変数なので、Updateメソッドからは使えません。

一方、hpはフィールドなので、StartUpdateなど複数のメソッドから使えます。

Unityでは、StartUpdateOnCollisionEnterなど複数のメソッドを使うことが多いため、ローカル変数とフィールドの違いを理解しておくことが特に重要です。

ただし、何でもフィールドにすると管理が難しくなります。1つのメソッド内でしか使わない値はローカル変数にし、キャラクターのHPや速度のように状態として保持したい値はフィールドにするのがおすすめです。

まとめ

C#のスコープとは、変数・メソッド・クラス・フィールドなどの名前が有効になる範囲のことです。

基本的には、波括弧{ }で囲まれたブロックがスコープの目印になります。ローカル変数は宣言したブロック内でのみ使え、if文やfor文の中で宣言した変数は、その外では使えません。

メソッド内で宣言した変数を別のメソッドから直接使うこともできません。複数のメソッドで同じ値を使いたい場合は、フィールド・引数・戻り値・プロパティなどを適切に使い分ける必要があります。

また、publicprivateなどのアクセス修飾子は、クラス外から見える範囲を制御するためのものです。スコープとは少し意味が違うため、混同しないようにしましょう。

C#のスコープで迷ったときは、次の3つを確認すると原因を見つけやすくなります。

変数をどこで宣言したか、どの波括弧の中にあるか、どこから使おうとしているか。

スコープを理解すると、エラーの原因を自分で見つけやすくなり、コードも読みやすくなります。C#初心者のうちは、変数を必要な範囲でだけ宣言し、スコープをできるだけ狭く保つことを意識してみてください。