C# 初級講座
VB2019 Visual Studio 2022

第18回 プログラムの構造

2022/8/28

この記事が対象とする製品・バージョン

Visual Studio 2022 Visual Studio 2022 対象です。
Visual Studio 2019 Visual Studio 2019 対象です。
Visual Studio 2017 Visual Studio 2017 対象外ですが、参考になります。
Visual Studio 2015 Visual Studio 2015 対象外ですが、参考になります。
Visual Studio 2013 Visual Studio 2013 対象外ですが、参考になります。
Visual Studio 2012 Visual Studio 2012 対象外ですが、参考になります。
Visual Studio 2010 Visual Studio 2010 対象外ですが、参考になります。
Visual Studio 2008 Visual Studio 2008 対象外ですが、参考になります。
Visual Studio 2005 Visual Studio 2005 対象外ですが、参考になります。
Visual Studio.NET 2003 Visual Studio.NET 2003 × 対象外です。
Visual Studio.NET 2002 Visual Studio.NET (2002) × 対象外です。
  Visual Studio Code 対象外ですが、参考になります。

 

目次

 

1.メンバー・クラス・名前空間

1-1.メンバーを呼び出すことで機能を実現できる

C#では、クラスまたは構造体のメンバーを呼び出すことで具体的な機能を実現します。

たとえば、DebugクラスのWriteLineメソッドでメッセージを表示したり、GraphicsクラスのDrawLineメソッドで絵を書いたり、BitmapクラスのSaveメソッドで画像をファイルに保存したりできます。

クラスまたは構造体のメンバーには、メソッドの他にプロパティやコンストラクターや演算子などもあります。

 

 

1-2.クラスまたは構造体は複数のメンバーを持つ

1つのクラスまたは構造体には複数のメンバーが存在しています。

たとえば、Graphicsクラスには直線を描く DrawLineメソッドの他に、背景を塗りつぶす Clearメソッドや、円を描くDrawEllipseメソッドなどたくさんのメソッドがあります。

 C#では、すべてのメンバー(つまり、すべてのメソッドやプロパティなど)は何かのクラスまたは構造体に実装されています。

 

1-3.クラスまたは構造体は名前空間で分類される

クラスまたは構造体は「名前空間」で分類されます。

たとえば、「System.Drawing」名前空間には、Windowsでのグラフィックス機能(GDI+)に使用するクラスや構造体が分類されています。「System.Windows.Forms」名前空間には Windows フォーム アプリで使用するクラスや構造体が分類されています。「System」空間には汎用で使用するクラスや構造体が分類されています。

この図ではイメージです。この図では3つの名前空間があり、それぞれ9つのクラスが分類されています。

実際には1つの名前空間の中にあるクラスや構造体の数は一定ではありません。たくさんのクラスや構造体を含む巨大な名前空間もあれば、小さな名前空間もあります。

原則としてクラスや構造体はどれかの名前空間に属することになっていますが、どの名前空間にも属さないクラスや構造体も定義することは可能です。これらを「グローバル名前空間に属している」と表現することもあります。

 

2.自作プログラムの構造

2-1.あなたのプログラムもルールに従います

上述の構造はあなたが作るプログラムにも当てはまります。

つまり、あなたが何かの機能をプログラムしようと思うと、メソッドまたはプロパティなどのメンバーを作る必要があります。

そして、それらを含むクラスまたは構造体を作る必要があります。

そして、それらを含む名前空間を作るべきです。

 

あなたが何か機能を実行するプログラムを作るのであれば、 どんなに小さなプログラムでもこれらが必要です。

 

まとめ

ルール1.あなたは名前空間を作るべきです。(しかし、これは絶対ではありません。)

ルール2.あなたは必ずクラスまたは構造体を作る必要があります。

ルール3.あなたは必ずメソッドなどのメンバーを作る必要があります。

 

 

 

2-2.実装方法の概略

メソッドを作る一般的な方法は void メソッド名(引数リスト) { } という構造を記述することです。

void の部分は、メソッドの戻り値によって変わります。また様々なキーワードがこれに付加されることが多いです。

もっと詳しいメソッドの作成方法は次回改めて説明します。ここでは、一例を紹介します。

public void SampleMethod()
{
    …
}

 

クラスを作る一般的な方法は class クラス名 { } という構造を記述することです。これにも様々なキーワードが付加されることがあります。

public class SampleClass
{
    …
}

 

名前空間を作る一般的な方法は namaspace 名前空間名 { } という構造を記述することです。

namespace SampleNamespace
{
    …
}

1つのファイル内で名前空間を1つしか定義しない場合は、次のような簡略な書き方もできます。最後に ; (セミコロン)がついている点に注目してください。


namespace SampleNamespace;

この書き方を「ファイルスコープの名前空間の宣言」と言います。C# 10 から導入された新しい機能です。C# 10 は .NET 6 をフレームワークとしているときの言語バージョンです。

実際のところ、1つのファイルで複数の名前空間を定義することはまずやらないので、.NET 6 を使うならこの簡略化した書き方がお勧めです。

 

 

2-3.プログラムの構造

以上の通りですので、上述の3つのルールを満たすために、これら全部を記述すると次のようなイメージになります。

namespace SampleNamespace
{
    public class SampleClass
    {
        public void SampleMethod()
        {
            …
        }
    }
}

C# 10(.NET 6)では、1つのファイル内で1つの名前空間しか定義しない場合次のようにも書いても同じ意味です。

namespace SampleNamespace;

public class SampleClass
{
    public void SampleMethod()
    {
        …
    }
}

 

1つの名前空間に2つのクラスを記述する場合は、次のように書けます。

namespace SampleNamespace
{
    public class SampleClass1
    {
        public void SampleMethod()
        {
            …
        }
    }

    public class SampleClass2
    {
        public void SampleMethod()
        {
            …
        }
    }
}

ファイルスコープの名前空間の宣言(=C# 10で導入された簡略された書き方)も可能です。その例は省略します。

 

3.Windows フォーム アプリの例

3-1.Windows フォーム アプリのプログラムの構造

典型的なWindowsフォームアプリで実例を見てみましょう。

試しに何か今まで作ったWindowsフォームアプリのサンプルプログラムなど開いて照らし合わせてみてください。

次のように namespace キーワードで大きく囲まれ、次に class の定義で大きく囲まれ、その中にコンストラクターやメソッドの定義が含まれていることがわかります。

コンストラクターの作成方法については今後説明する予定なのでここではいったん無視しましょう。

このプログラムではButtonを2つ配置してClick時にメッセージを表示すようになっています。

Clickイベントのハンドラーはプログラムの構造から見るとメソッドです。つまり、button1_Click をプログラムしているとき、あなたは Form1 クラスの button1_Click メソッドをプログラムしているのです。

これが前述したように「あなたのプログラムもルールに従います」ということの意味です。

 

3-2.public

ところで、クラスの定義が少し複雑なので簡単に補足しておきます。

Form1 クラスを定義する最小限のコードは class Form1 です。

public, partial, : Form という記述は追加の記述です。

public (読み方:public=パブリック)はここで定義するクラスを、他のクラスから呼び出せることを意味しています。他のクラスなどどこにも見当たらないように思うかもしれませんが、よく見ると、Windowsフォームアプリには Form1.cs の他に Program.cs というプログラムが含まれているのがわかります。

Program.csの内容はフレームワークのバージョンによって少し違いますが、だいたい次のようなものです。

namespace WinFormsApp1
{
    internal static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            // To customize application configuration such as set high DPI settings or default font,
            // see https://aka.ms/applicationconfiguration.
            ApplicationConfiguration.Initialize();
            Application.Run(new Form1());
        }
    }
}

ここでは ProgramクラスとMainメソッドが定義されているのが読み取れると思います。

C#のプログラムは Main メソッドから開始するというルールがあります。つまり、ここで定義されている Main メソッドはプログラム開始の一番最初に実行されるところです。この最初に実行されるところのことを「エントリーポイント」と呼びます。Mainメソッドはエントリーポイントの役目があるので特殊です。

この中で Application.Run(new Form1()); という部分が簡単に言うと 「Form1 を実行しろ」という命令です。このとき、new を使って Form1 のコンストラクターを呼び出しているのがわかりますね。

ひるがえって Form1のコンストラクターの定義をよく見てみると、こちらにも public がついています。

public Form1()
{
    InitializeComponent();
}

このpublicの効果で、Programクラスという外部のクラスからこのコンストラクターを呼び出すことができています。

クラスは通常、どこかから呼び出してもらわないと何も機能が動かないので public で定義します。(別途機会があれば説明しますが)入れ子になっているクラスの場合はpublic以外で定義する場合もあります。

 

3-3.partial

partial(読み方:partial=パーシャル)は、この定義が複数のファイルに分割されていることを意味します。この場合、ファイルとはForm1.csのことです。

1つのクラスが1つのファイルだけで完結している場合は partial は不要です。

Form1クラスの場合は、Visual Studioのデザイナーでボタンなどをペタペタ貼り付けたり、プロパティウィンドウでプロパティを変更した時にForm1.Designer.csファイルにプログラムが自動的に生成されます。このことは入門講座第7回で説明しています。

この自動生成されるプログラムもForm1なので、同じForm1のプログラムがForm1.cs と Form1.Designer.cs の2ファイルにまたがって存在します。

そのため、定義に Partial がついています。

Partialはこのように自動生成されるプログランと自分で入力するプログラムが混ざらないようにファイルを分けるというのが主な目的なので、自分で何もかもプログラムしている場合は Partial キーワードでクラスを分割する必要はほぼないでしょう。

 

3-4. : Form

最後の : Form の部分は、Form1 クラスは Form クラスの機能を継承するということを指示しています。

Formクラスは、サイズ変更可能なウィンドウでタイトルバーや最大化ボタンや、最小化などさまざまな機能を備えています。これらのフォームに共通する機能はマイクロソフトのプログラマーが事前にフレームワークの中に Form クラスとして用意しておいてくれています。

これらの機能をすべて Form1 クラスで利用するというのが継承という強力な機能です。

継承機能を使うと、自作のクラスに1行もプログラムしなくても、他のクラスの機能をそっくり持っているクラスを簡単に作成することができます。

また、この機能のおかげで、プログラマーはよくある機能を自分でプログラムする必要がなくなり、自分が興味のある機能だけを記述すればよくなります。

 

プログラムを実行されたときに開始されるのは、このProgram.cs の中のプログラムです。この中でnew キーワード で Form1 クラスのコンストラクターを呼び出している部分があります。

 

4.コンソールアプリの例

4-1.最上位レベルのステートメント

名前空間・クラス・メンバーというプログラムの構造はすべての C# のプログラムにあてはまります。当然コンソールアプリでも同じです。

ただし、.NET 6 から「最上位レベルのステートメント」という機能が導入されており、見かけ上これらの構造を記述しない書き方ができるようになっています。Visual Studio 2022 で .NET 6 でコンソールアプリを作成すると既定でこの最上位レベルのステートメント機能が使用されます。(最上位レベルのステートメント機能はコンソールアプリの特有の機能というわけではなく、C#の機能です。他の種類のアプリケーションでも使用できます。)

ちょっとプログラムを試したいだけの時に名前空間やクラスを記述するのは面倒ですし、初心者が勉強するときにもはじめのうちは知らなくて良いプログラムが隠されているので、学習するテーマが絞れて勉強しやすくなります。

 

テンプレートで最上位レベルのステートメント機能を利用するかどうかは、コンソールアプリ作成時にチェックボックスで選べます。

このチェックボックスは Visual Studio 2022 に後から追加された機能なので、アップデートしないで初期のバージョンの Visual Studio 2022 を使用している場合は、このチェックボックスが存在していないか、英語で表示されているかもしれません。

※このスクリーンショットは少し画像を切り貼りして実際のよりサイズを小さくしています。(本物はもっと余白が広いです。)

既定ではこのチェックボックスはオフです。オフの場合は、最上位レベルのステートメントを利用したテンプレートが使用されます。

このチェックボックスは最初に自動生成されるプログラムに影響するだけなので、自分でプログラムを書き換えて最上位レベルのステートメントを使用したりやめたりすることもできます。

 

Visual Studio 2022 の 機能で簡単に最上位レベルのステートメントを利用しない形式に切り替えることもできます。

この機能はバージョン 17.3 以降で利用可能です。Visual Studio 2022 をアップデートしていない場合は使用できません。

エディターの画面で2秒くらい何もしないと左側にクイック操作のアイコン(ドライバーのマーク)が表示されます。

Alt + Enter または Ctrl + . でも表示できます。その場合は電球マークです。

このアイコンをクリックして、「'Program.Main'スタイル プログラムに変換」をクリックすると、最上位レベルのステートメントを使用しない形式に変換されます。

 

4-2.隠されているコード

チェックボックスをオフにした状態(つまり、既定の状態)でコンソールアプリを作成すると次のようなたった1行(コメント行を含むと2行)のプログラムになります。

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

この1行のプログラムだけで動作します。

ここでは namespace や class や メソッドなどの基本的な構造が記述されていません。記述されてはいませんが、このプログラムはProgramクラスのメソッドです。見えないだけで、C# のルールに従った最低限のプログラムの構造は存在しているのです。

 

現在実行中のクラスとメソッドと名前空間を表示するプログラムを加えて実験してみましょう。リフレクションという機能を使うことでそれができます。この機能の詳細は割愛します。

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

var method = new System.Diagnostics.StackFrame(0, false).GetMethod();
string methodName = method!.Name;
string className = method.DeclaringType!.Name;
string namespaceName = method.DeclaringType!.Namespace!;

Console.WriteLine($"名前空間:{namespaceName}"); // 何も表示されません。
Console.WriteLine($"クラス:{className}"); // Program
Console.WriteLine($"メソッド:{methodName}"); // <Main>$

これを実行すると次のように出力されます。

Hello, World!
名前空間:
クラス:Program
メソッド:<Main>$

ここから読み取れることは、このプログラムは Programクラスの <Main>$ (という変わった名前の)メソッドであり、名前空間はないということです。ILSpyなどのツールを使ってもっと詳しく中を見ると<Main>$メソッドにはargsという名前の引数があり、これは文字列型の配列であることもわかります。

<Main>$ という名前のメソッドは C# では定義できないのですが、C#を実行しているランタイムはC#以外の言語にも対応した構造になっておりC#のルールに従っていない名前のメソッドも持つことができます。おそらく、Visual Studioが自動的に生成された名前であることがわかるようにわざとC#にルールに従っていないメソッド名を生成しているのだと思います。

 

ということで、記述されていない部分をあえて書いてみると、このプログラムは次のプログラムとほぼ同じ意味です。メソッド名はC#のルールに従って Main にしました。厳密には実際に自動生成されるものとは異なる部分もあります。

internal static class Program
{       
    static void Main(string[] args)
    {
        // See https://aka.ms/new-console-template for more information
        Console.WriteLine("Hello, World!");
    }
}

実際にまるごとこのプログラムに置き換えても動作します。

これを見ると Windows フォーム アプリ でも登場する Programクラスとほとんど同じものであることがわかります。

 

プロジェクトを作成するときに「最上位レベルのステートメントを使用しない」にチェックを入れるた場合は、次のようなプログラムが生成されます。

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
        }
    }
}

namespaceを使って名前空間が定義されていること以外は、最上位ステートメントを使用する場合と同じですね。

 

4-3.最上位レベルのステートメントは1つだけ

1つのプロジェクトで最上位レベルのステートメントは1つだけしか使用できません。

もし、2つ以上記述してしまうと次のエラーが発生します。

エラー CS8802 トップレベルのステートメントを持つことができるのは、1 つのコンパイル ユニットのみです。

このエラーメッセージの「トップレベルのステートメント」とは最上位レベルのステートメントのことです。

 

どのような状況か、ピンとこないと思うので、もう少し掘り下げます。

少し大きめのプログラムを作ろうとすると複数のファイルにプログラムを分けることをよくやります。

たとえば、通常は1つのファイルに1つのクラスを記述しまから、複数のクラスを定義するとファイルも複数になります。

自分でクラスを定義するごく簡単な方法は既に紹介しました。

最上位レベルのステートメントを使用しているコンソールアプリに新しくファイルを追加してクラスを定義することを考えてみましょう。

 

コンソールアプリには既定で Program.cs というファイルがあります。

新しく追加するファイルには次のような記述をして、名前空間とクラスとメンバーを定義するべきです。

実際 Visual Studio で追加操作を行うとこれと同じ構造で名前空間と空のクラスのコードを自動生成します。

namespace SampleNamespace;
{
    public class SampleClass
    {
        public void SampleMethod()
        {
            System.Diagnostics.Debug.WriteLine("SampleMethod");
        }
    }
}

これは何の問題もありません。

が、もし、この新しいファイル内で名前空間の定義とクラスの定義を省略して、たとえば、次のような記述をするとダメです。


System.Diagnostics.Debug.WriteLine("SampleMethod");

これが許されるのは最上位レベルのステートメントの場合だけです。

しかし、最上位レベルのステートメントは既に Program.cs で使用しているので、この新しいファイルでこの記述をすると、Visual Studio は最上位レベルのステートメントが2つあると解釈します。

それで冒頭にあるエラーを生成します。

エラー CS8802 トップレベルのステートメントを持つことができるのは、1 つのコンパイル ユニットのみです。

 

5.練習問題

問1.C#でプログラムするほとんどの場合、あなたのプログラムで定義されるものはどれでしょうか?

名前空間
クラス
メンバー
演算子
継承
最上位レベルのステートメント

 

 

問2.C#のプログラムはどこから開始されますか?
Load
先頭
Main

問3.C#のプログラムの記述量を減らせそうなのものをすべて選んでください。

ファイルスコープの名前空間
partialでのクラス分割
最上位レベルのステートメント
アクセス修飾子

 

 

次の回では、メソッドの作り方を説明します。

C#初級講座講座 次の回へ