前回は、C++でWindows SDKベースのプログラムで「WM_PAINTイベント割り込みで、ウィンドウのクライアントエリアの中央にグラフィックで文字列出力を行う」プロジェクト(HelloWorld3)を作りました。

 

今回はC#を使って同じようなものを作るのですが、C#にはプログラミングプラットフォームがWinFormsWFPの二つがあり()、初心者やC#をよく知らない人は当惑してしまいます。(事実、5年前の私も「???」でした。)

:更にWinFormsでは「Windowsフォームアプリ」と「Windowsフォームアプリケーション(.NET Framework)」、WPFでは「WPFアプリケーション」と「WPFアプリ(.NET Framework)」と更に選択肢がありますが、「何がどう違い、どういう場合に選ぶべきか」が全く分かりません。その為ご本家の説明を求めても、

 

 

ここから長々と「(専門用語の)寿限無寿限無...」を読まなければならないと思うと、心が折れます。

 

そうだっ!Copilot君にまとめさせよう!」ということで質問すると、ズラズラと長い説明が返ってきたので、(誤解を招く)危険を承知で私が短く纏めると以下の通りかと考えます。(2 x 2 = 4通り)

 

1.WinForms 対 WFP

 

要すれば、従来(Windows 95以降のGDIやXP以降のGDI+)のグラフィックを直接GPUを操作できるDirect X 技術に置き換え、クロスプラットフォームが可能となるようにディスプレー縦横比が変わってもキチンと表示できるようなUIを導入し、その開発にプログラミング用マークアップ言語(XAML)を使うようになった為、グラフィックデザイナーやコーダー等それぞれの領域のプロの分業がより効率的になった、とでもいえましょうか?(飽くまで私見ですが...)

 

2..NET 対 .NET Framework(これはCopilot君任せ)

(1) .NET(.NET 5 以降)版

  • 「Windows フォームアプリ」
  • 「WPF アプリケーション」

特徴

  • 新しい .NET(.NET 5+)ランタイム を使用
  • クロスプラットフォームではないが、.NET の最新機能・最適化を利用可能
  • 将来性があり、Microsoft の主流ライン
  • ただし 古い .NET Framework 専用 API やライブラリは使えない場合がある

(2) .NET Framework 版

  • 「Windows フォームアプリケーション(.NET Framework)」
  • 「WPF アプリ(.NET Framework)」

特徴

  • .NET Framework 4.x を使用
  • Windows 専用、レガシー資産との互換性が高い
  • 古い API、COM、Win32、既存 DLL との連携が容易
  • ただし 開発は保守モード(新機能はほぼ追加されない)

いかがでしょうか?少しは「分かった」感がでてきましたでしょうか?

 

しかし、

 

いくら理屈をこねても仕方がないので、「説明なしにズラズラとメニューを並べるVisual Studioのテンプレートの大体の概観が得られた(かな?)」所で、次回からは「現場、現物、現実」の現主義的に実際にVisual Studioにテンプレートを作らせて、それを見てゆきましょう。

 

さてさて、前回で「『Hello World!巡り』-コンソール編」をC++、C#共に終了しましたので、今回からデスクトップテンプレートを使って旅を続けていきたいと思います。

 

先ずはC++から。

 

 

選択したのは「Windows デスクトップウィザード」というやつです。「次へ」ボタンを押して進むと、

 

はて?

 

 

成程。「Windows Desktopで使えるプログラムだから、ウィンドウアプリのみならず、コマンドプロンプト(いわゆる「DOS窓」)で使えるコンソールアプリや.DLL、.LIBも含む」という意味ね。(

:だったら「コンソールアプリを作る入口がダブっているということ?それとも違いがあるの?」という突っ込みができそうです。最初にVisual Studioを使った5年前と全く違うのは私にはCopilotというAIのお供(友?)がいること。彼に聞くと、次の通りとのこと。(矢張りここにもユーザーの知らない黒子達がいっぱいいました。)

 

とはいえ、私は「デスクトップアプリケーション(.exe)(Win32 API)」を選択して進みます。

 

【全体イメージ】

 

小さくて見難いですが、今度はソースファイル(HelloWorld3.cpp)にびっしりコードが書き込まれているのがわかるでしょう。また一見でそれがWindows SDK)ベースのコードであることがわかります。

Windows SDK(Windows Software Development Kit )とはC/C++言語でWin32 APIを使ってプログラミングをするためのヘッダーファイル等が一式セットになったものです。MFC(Microsoft Foundation Class)等を使っていないのでWindowsがウィンドウプログラムをどのように動かしているのか等、基礎的な構造や仕組みを学べます。至極端折って言えば、ウィンドウプログラムはマウスクリックやキー入力、ウィンドウのサイズ変更等という「入力イベント」の割り込みに対し、対応する「関数(メソッド)」を都度処理するプログラムであり、プログラムの起動も"WinMain"というエントリーポイント関数になります。

 

コンソールプログラムと大きく異なるのは関連ファイルの多さです。↑のイメージの左上にある「ソリューションエクスプローラー」の拡大版を載せます。

 

 

御覧のようにヘッダーファイルが4本、リソースファイルとしてリソーススクリプト(.rc)とアイコンファイルが二つあります。(「外部依存関係」はWin32 APIがズラズラと並び膨大です。)

 

(1)framework.h

HelloWorld3のプログラム(ウィンドウズプログラミング)用のSKDとC++ファイブラリーのヘッダーファイルがまとめられていました。

 

// header.h : 標準のシステム インクルード ファイルのインクルード ファイル、
// またはプロジェクト専用のインクルード ファイル
//

#pragma once

#include "targetver.h"
#define WIN32_LEAN_AND_MEAN
            // Windows ヘッダーからほとんど使用されていない部分を除外する
// Windows ヘッダー ファイル
#include <windows.h>
// C ランタイム ヘッダー ファイル
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>

 

(2)HelloWorld3.h

単に以下(3)のファイルを読み込む為だけのものでした。(複雑なもプログラムではもっと他のヘッダー等が追加されるのかもしれません。)

 

#pragma once

#include "resource.h"

 

(3)Resource.h

これはWin32リソースにID番号を設定するものです。(BCCSkeltonでは"Res(プロジェクト名).h"となる奴)

尚、プリプロセッサー(if~endif)についてはインデントがされていないので、色で対応を示しておきます。

 

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ で生成されたインクルード ファイルです。
// 次で使用: HelloWorld3.rc

#define IDS_APP_TITLE            103

#define IDR_MAINFRAME            128
#define IDD_HELLOWORLD3_DIALOG    102
#define IDD_ABOUTBOX            103
#define IDM_ABOUT                104
#define IDM_EXIT                105
#define IDI_HELLOWORLD3            107
#define IDI_SMALL                108
#define IDC_HELLOWORLD3            109
#define IDC_MYICON                2

#ifndef IDC_STATIC
#define IDC_STATIC                -1

#endif
// 新しいオブジェクトの次の既定値
//

#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NO_MFC                    130
#define _APS_NEXT_RESOURCE_VALUE    129
#define _APS_NEXT_COMMAND_VALUE        32771
#define _APS_NEXT_CONTROL_VALUE        1000
#define _APS_NEXT_SYMED_VALUE        110

#endif
#endif
 

(4)targetver.h

良く分かりませんが、Windows SDKの「(利用できるもののうち、最も新しい)最適化されたバージョンを選択する」為のヘッダーファイルのようです。

 

#pragma once

// SDKDDKVer.h をインクルードすると、利用できる最も高いレベルの Windows プラットフォームが定義されます。
// 以前の Windows プラットフォーム用にアプリケーションをビルドする場合は、WinSDKVer.h をインクルードし、
// サポートしたいプラットフォームに _WIN32_WINNT マクロを設定してから SDKDDKVer.h をインクルードします。

#include <SDKDDKVer.h>
 

(5)HelloWorld3.ico、small.ico

二つとも同じデザインの自動生成されたアイコンファイルで次の(6)HelloWOrld.rcから参照されます。

 

(6)HelloWorld3.rc

自動生成されたリソーススクリプトファイルです。

 

//Microsoft Visual C++ で生成されたリソース スクリプトです。
//

#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE リソースから生成されました。
//

#ifndef APSTUDIO_INVOKED
#include "targetver.h"
#endif
#define APSTUDIO_HIDDEN_SYMBOLS
#include "windows.h"
#undef APSTUDIO_HIDDEN_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN)
LANGUAGE 17, 1

/////////////////////////////////////////////////////////////////////////////
//
// アイコン
//

// アプリケーション アイコンをすべてのシステム上で維持するために、最も小さい
// ID 値のアイコンが最初に配置されます。


IDI_HELLOWORLD3       ICON         "HelloWorld3.ico"
IDI_SMALL               ICON         "small.ico"


/////////////////////////////////////////////////////////////////////////////
//
// メニュー
//

IDC_HELLOWORLD3 MENU
BEGIN
    POPUP "ファイル(&F)"
    BEGIN
        MENUITEM "終了する(&X)",                IDM_EXIT
    END
    POPUP "ヘルプ(&H)"
    BEGIN
        MENUITEM "バージョン情報(&A)...",           IDM_ABOUT
    END
END


/////////////////////////////////////////////////////////////////////////////
//
// アクセラレータ
//

IDC_HELLOWORLD3 ACCELERATORS
BEGIN
    "?",            IDM_ABOUT,              ASCII,  ALT
    "/",            IDM_ABOUT,              ASCII,  ALT
END


/////////////////////////////////////////////////////////////////////////////
//
// ダイアログ
//

IDD_ABOUTBOX DIALOGEX 0, 0, 170, 62
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "バージョン情報 HelloWorld3"
FONT 9, "MS UI Gothic"
BEGIN
    ICON            IDI_HELLOWORLD3,IDC_STATIC,14,14,21,20
    LTEXT           "HelloWorld3, バージョン 1.0",IDC_STATIC,42,14,114,8,SS_NOPREFIX
    LTEXT           "Copyright (c) 2025",IDC_STATIC,42,26,114,8
    DEFPUSHBUTTON   "OK",IDOK,113,41,50,14,WS_GROUP
END


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    IDD_ABOUTBOX, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 163
        TOPMARGIN, 7
        BOTTOMMARGIN, 55
    END
END

#endif    // APSTUDIO_INVOKED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE
BEGIN
    "#ifndef APSTUDIO_INVOKED\r\n"
    "#include ""targetver.h""\r\n"
    "#endif\r\n"
    "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
    "#include ""windows.h""\r\n"
    "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
    "\0"
END

3 TEXTINCLUDE
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED

/////////////////////////////////////////////////////////////////////////////
//
// 文字列テーブル
//

STRINGTABLE
BEGIN
   IDC_HELLOWORLD3   "HELLOWORLD3"
   IDS_APP_TITLE       "HelloWorld3"
END

#endif
/////////////////////////////////////////////////////////////////////////////

#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE リソースから生成されました。
//
/////////////////////////////////////////////////////////////////////////////

#endif    // not APSTUDIO_INVOKED
 

(7)HelloWorld3.cpp

そして最後に自動生成された(ユーザー用の)プログラム「本体」です。ウィンドウにグラフィックで文字出力を行うために、WM_PAINTイベント割り込みでクライアントエリアの中央に文字列を書いています。

 

// HelloWorld3.cpp : アプリケーションのエントリ ポイントを定義します。
//

#include "framework.h"
#include "HelloWorld3.h"

#define MAX_LOADSTRING 100


// グローバル変数:
HINSTANCE hInst;                                // 現在のインターフェイス
WCHAR szTitle[MAX_LOADSTRING];                  // タイトル バーのテキスト
WCHAR szWindowClass[MAX_LOADSTRING];            // メイン ウィンドウ クラス名

// このコード モジュールに含まれる関数の宣言を転送します:(解説:「転送」が意味不明ですが、所謂「プロトタイプ宣言」です。)
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);


//解説:ウィンドウプログラムのエントリ^ポイント関数です。'w'がついているのはユニコード対応であることを示しています。
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);


    // TODO: ここにコードを挿入してください。

    // グローバル文字列を初期化する
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_HELLOWORLD3, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance); 
  //解説:これはこのウィンドウをOSに登録しています。

    // アプリケーション初期化の実行:( 解説:ウィンドウの生成と表示を行っています。)
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }


    //解説:アクセラレーションテーブルを読み込んでいます。
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_HELLOWORLD3));

    MSG msg;
    // メイン メッセージ ループ:(解説:このループが続く限り、ウィンドウが開いています。メッセージWM_CLOSEが呼ばれるとウィンドウを閉じ、続いてWM_DESTROYでウィンドウが廃棄されます。)
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}


//
//  関数: MyRegisterClass()
//
//  目的: ウィンドウ クラスを登録します。
//

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_HELLOWORLD3));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_HELLOWORLD3);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    return RegisterClassExW(&wcex);
}


//
//   関数: InitInstance(HINSTANCE, int)
//
//   目的: インスタンス ハンドルを保存して、メイン ウィンドウを作成します
//
//   コメント:
//
//        この関数で、グローバル変数でインスタンス ハンドルを保存し、
//        メイン プログラム ウィンドウを作成および表示します。
//

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance;
// グローバル変数にインスタンス ハンドルを格納する
   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
   if (!hWnd)
   {
      return FALSE;
   }
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
   return TRUE;
}


//
//  関数: WndProc(HWND, UINT, WPARAM, LPARAM)
//   解説:所謂ウィンドウコールバック関数で↑にあったメッセージループと対になっています。
//  目的: メイン ウィンドウのメッセージを処理します。
//
//  WM_COMMAND  - アプリケーション メニューの処理
//  WM_PAINT    - メイン ウィンドウを描画する
//  WM_DESTROY  - 中止メッセージを表示して戻る
//

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);

            // 選択されたメニューの解析:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);

            // TODO: HDC を使用する描画コードをここに追加してください...
 

            //解説:文字列の描画部分だけは私が書きました。Win32APIでよく使われる文字列描画関数は
            //BOOL TextOut(HDC hdc, int x座標, int y座標, LPC(W)STR 文字列ポインター, int 文字列長);
            //ですが、
            //int DrawText(HDC hdc, LPC(W)TSTR 文字列ポインター, int 文字列長, LPRECT 文字列描画RECT構造体へのポインター, UINT 書式設定);
            //も使いやすいと思われます。

            TCHAR str[] = TEXT("Hello World!");
            RECT rec;
            GetClientRect(hWnd, &rec);

            DrawText(hdc, str, -1, &rec, DT_CENTER | DT_SINGLELINE  | DT_VCENTER);
           
EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}


// バージョン情報ボックスのメッセージ ハンドラーです。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;
    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;

}

 

いかがだったでしょうか?Windows SDKを使ってプログラミングすると、C/C++文法とWin32 APIだけを使ったプログラミングになるため、何をしているのかが分かりやすい反面、細かいところまでコーディングしなければならないので、コード量が増えることがお判りいただけたかと存じます。その意味で大部分が自動生成されるVisual Studioはありがたいのですが、

 

「どこに何を書くの?」

 

ということがわからない段階では結構ハードルが高いのではないか、と愚考します。

 

いずれにせよ、これをビルドして実行すると、こういう成果物が現れます。(自動生成リソースはアイコン、メニューとバージョンダイアログです。)

 

次はC#でやってみましょう。

 

新PCを迎えたために、旧PCをVisual Studio専用機にして【Visual Studdio探検記】を始めたら、案の定大作になりそうなので、ここで一息、また食べ物の話を入れましょう。

 

昨日(2025年12月20日)のお昼、突然「スパゲッティをアラビアータソースで食べたいっ!」と思い立ちました。

 

しかし、スパだけだと栄養の偏りがあるので、野菜とソーセージの薄切りを入れたチキンコンソメスープ、サラダを用意するのみならず、スパゲッティもアーリオオーリオ(Aglio Olio)にひき肉と野菜を入れて炒め、それに市販のアラビアータソースを絡めた結果、

 

見た目が「ナポリタン」

 

に見えるスパゲッティアラビアータになりました。これでいいのか?

 

これでいいのだっ!

 

「Hello World!巡り」-コンソール編(C++)をやったので、当然C#編も続けてやりましょう。

 

ここでプログラミング言語としてのC#についてご存じでない方のために簡単に説明すると、

 

(1)一番初めは、「1972年にAT&Tベル研究所のデニス・リッチーが主体となって開発した汎用プログラミング言語 (wiki)」として「C」言語が開発されました。これはアッセンブラーのような速いネイティブコード(機械語)をコンパイルする柔軟性のある言語でしたが、その美点の表裏のような結果として(保護メモリーに直接アクセスしたり、キャストなどによってかなり悪いこともできる)低級言語(細かい処理までできるということは、それだけ書かなければならない処理が多くてコード量が増える)であった為、

 

(2)Cを高級言語化した「クラス」を利用できるオブジェクト指向言語("C with Classes")の開発が1979年から開始され、1983年には「Cを(進化を彷彿させる)インクリメント(++)させた言語」という意味で"C++"という名称となり、1985年に商用版がリリースされました。(wiki

 

(3)更に(旧BorlandのTurbo Pascalや、当時WYSWYGで頭抜けていたDelphiを開発したアンダース・ヘルスバーグを筆頭として多数のDelphi開発陣が参加し、)Windowsの.NET Framework上で動作することを前提としてマイクロソフトが開発したのがC#C++を進化させてインクリメントした言語、"C++++"→"C#"という意味らしいで、Javaに似た構文、C++に比べて扱いやすくプログラムの記述量も少なく、2016年6月からはクロスプラットフォームな.NETランタイム上で動作します。(wiki

 

ただし、

 

「高級」になった分、またプラットフォームが.NET Framework→.NET_Core→.NETへ進化する過程、更に(旧いWin32APIベースの)WinFormsから(XAMLによるUI定義やDirect3Dを活用したグラフィック等の新技術を反映した)WPFの導入等により、ライブラリーやソリューション/プロジェクト管理が複雑化した印象が拭えません。

 

例えば

 

C++HelloWorld1では、ソリューションのフォールダーの下には64bitの実行ファイルが入る「x64」、プロジェクトの「HelloWorld1」のフォールダーがあり、「HelloWorld1」の下にはソースファイルや管理ファイルの下にまた「x64」があって、その中に「Debug」と「Release」という実行ファイルのフォールダーが並ぶだけでした。(その下にもゴチャゴチャあるのですが省略)

 

所が

 

これがC#HelloWorld2になると、「x64」のフォールダーがなくなり、管理用の「.vs隠しフォールダーが作られ、その下に管理フォールダーとファイルが入ります。またプロジェクトフォールダー(HelloWorld2)の下にはソースファイルと管理ファイルの他に「bin」(実行ファイル関連)、「obj」(実行ファイル作成用のリンクファイル関連-実行ファイルを含む)、「properties」(実行ファイルの属性情報等)が入るという、系統建てられてはいてもだいぶ複雑な感じになっています。

 

【全体イメージ】

 

Visual Studioの画面レイアウトはC++のHelloWorld1と変わりませんが、ソリューションエクスプローラーとソースコードは大きく異なります。

 

先ず、Visual Studioが「ここにプログラムをかけ」というファイル(ユーザープログラム本体)Program.cs(↑のエディターの部分)になります。しかし、ソリューションエクスプローラーで中身が見れるファイルだけでも、

 

【プログラム部分

(1)Assembly.cs

プロジェクトの「Properties」として、開発するアセンブリ(Assembly-の属性情報(Win32時代のリソースの一種であったVERSION情報等)がプログラムの一部としてビルドされます。

:「Assemblyが何か」はご本家の説明では分かり辛いと思いますが、.NET環境における「展開、バージョン管理、再利用、アクティブ化スコープ、およびセキュリティアクセス許可」情報が組み込まれ、仮体した実行ファイル(*.exe、*.dll等)とお考え下さい。

 

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;


// アセンブリに関する一般的な情報は、次の方法で制御されます。
// 制御されます。アセンブリに関連付けられている情報を変更するには、
// これらの属性値を変更します。

[assembly: AssemblyTitle("HelloWorld2")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("HelloWorld2")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]


// ComVisible を false に設定すると、このアセンブリ内の型は COM コンポーネントから
// 参照できなくなります。COM からこのアセンブリ内の型にアクセスする必要がある場合は、
// その型の ComVisible 属性を true に設定します。

[assembly: ComVisible(false)]

// このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります
[assembly: Guid("6e3229e4-d7f3-42f9-b4b5-fe1b707d5d27")]

// アセンブリのバージョン情報は次の 4 つの値で構成されています:
//
//      メジャー バージョン
//      マイナー バージョン
//      ビルド番号
//      リビジョン
//

[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

 

(2)App.config

このC#はWinFormsで作られますが、アプリケーション(プログラム)の基盤情報(エンコーディングや.NETのプラットフォーム種別)にかかわるXAMLファイルになります。

 

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>
</configuration>

 

(3)Program.cs

ここで初めてユーザープログラムが現れます。

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HelloWorld2
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(">>> HelloWorld2 <<<");
 //解説:これは私が書きました。
            Console.WriteLine("Hello World!");  //解説:これは私が書きました。
            Console.Read();                     //解説:これは私が書きました。(コンソールプログラムを終わらせない為)
        }
    }
}

 

C#のコンソールプログラムも"Hello World!"のビルトインコードがあるかと思ったのですが、ありませんでしたね。

 

これを「ビルド」-「ソリューションのビルド」、「デバッグ」-「デバッグの開始」(または「デバッグなしで開始」)で実行すると、

 

 

となります。

 

お判りでしょうか?

単に"Hello World!"という文字列をコンソールに表示するだけでも、Visual Studioという裏方組織は「ユーザーに知られることなく、様々な役割を、様々な黒子(「闇の実行部隊」)が支えている、ということを。

 

ps. 単なる"Hello World!"のプログラム紹介をここまで書くだけで疲れてしまいましたが、まだまだ冒険の旅は続きます。今度はいよいよウィンドウプログラムになります。

 

さて、前回書いたように、本格的に冒険の旅へ出かける前に小手調べとして

 

Hello World!

 

を出力する定番プログラムを試してみることにしました。

 

1.C++プログラム

先ずはC++で「コンソールアプリ」テンプレートでコンソール出力プログラムを作ってみます。(

:「空のプロジェクト」でもよかったのですが、Visual Studioのプロジェクトテンプレートには予めプロジェクト管理ギミックが仕掛けられていると思うので、それも見たいと思い、「コンソールアプリ」にしました。

 

すると自動生成された(ユーザーのプログラミング用)テンプレートは「まんま、Hello World」になっていたので、二行だけ追加しました。

 

【全体イメージ】

 

小さくてみにくいのですが()、フルスクリーンのVisual Strudioの中に、

 

(1)最も左に隠れているのが「ツールボックス

(2)次に上部にタブ付きのエディター(これは本体の一部のようです)、その下部に「出力」と「エラー一覧」(↑のイメージではタブにまとまってますが、各独立画面、独立2画面にすることも可能。)

(3)右に「ソリューションエクスプローラー」(そのタブとして「プロパティマネージャー」「リソースビュー」と「Git変更」があります)、さらにその下部に「プロパティウィンドウ」があります。

(4)これらはいったん閉じて、再度開くときにはメニューの「表示」-「ソリューションエクスプローラー」、「Git変更」、「プロパティウィンドウ」および「その他のウィンドウ」-「リソースビュー」、「プロパティマネージャー」から呼び出せます。

:というよりも、Visual StudioというIDEを画面一杯に広げても、テンコ盛りの機能ですべて埋め尽くされてしまうからです。

 

ソリューション(開発対象の親分)エクスプローラーの中を見てください。まずプロジェクト親分の手下の若頭みたいなもの、今回はHelloWorld01)があり、プロジェクトには「参照」「外部依存関係」「ソースファイル」「ヘッダーファイル」「リソースファイル(実行部隊の協力先と舎弟ですね)が続きます。プログラムはiostreamというヘッダーファイルを読んでいますが、参照ヘッダーファイルフォールダーは空です。(おそらくユーザーのヘッダーファイルを対象とするのでしょう。)外部依存関係はシステムやC++ライブラリー(今回はiostream)の関数がずらっと並びます。ソースファイルには自動生成ファイルが入り、リソースファイルには何も入っていません。

 

【プログラム部分ソースファイル-HelloWorld01.cpp

// HelloWorld1.cpp : このファイルには 'main' 関数が含まれています。プログラム実行の開始と終了がそこで行われます。
//


#include <iostream>

int main()
{
    std::cout << ">>> HelloWorld1 <<<" << std::endl;   //解説:これは私が書きました
    std::cout << "Hello World!\n";
    std::cin.get();   //解説:exeプログラムを実行するとすぐに消えるので、これは私が書きました
}

// プログラムの実行: Ctrl + F5 または [デバッグ] > [デバッグなしで開始] メニュー
// プログラムのデバッグ: F5 または [デバッグ] > [デバッグの開始] メニュー

// 作業を開始するためのヒント: 
//    1. ソリューション エクスプローラー ウィンドウを使用してファイルを追加/管理します 
//   2. チーム エクスプローラー ウィンドウを使用してソース管理に接続します
//   3. 出力ウィンドウを使用して、ビルド出力とその他のメッセージを表示します
//   4. エラー一覧ウィンドウを使用してエラーを表示します
//   5. [プロジェクト] > [新しい項目の追加] と移動して新しいコード ファイルを作成するか、[プロジェクト] > [既存の項目の追加] と移動して既存のコード ファイルをプロジェクトに追加します
//   6. 後ほどこのプロジェクトを再び開く場合、[ファイル] > [開く] > [プロジェクト] と移動して .sln ファイルを選択します

(二行だけ追加しましたが)後はメニューの「ビルド」ー「ソリューションのビルド」()を行い、「出力」と「エラー一覧」で正常にビルドできたことを確認して、「デバッグ」-「デバッグの開始(または「デバッグなしで開始」)」を選択すると、

:ソリューションのビルドを行うと、その傘下のすべてのプロジェクトがビルドされる、ということです。個別にプロジェクトをビルドすることもできます。

 

 

と表示されます。

 

「あー、簡単なんだ。」

 

と思われる方は3点ご注意を。

 

."Hello World!"プログラムは世にC言語を知らしめるためにカーニハンとリッチーが考え出した陰謀であり、現実のプログラミングはそんなに簡単なものではない、ということと、

 

.何しろVisual Studioは、

 

複数のスタッフが共同、協働してプロジェクト、そしてソリューションを完成させて行くことを想定した「プロの環境」なので、実際のプログラミングのみならず、管理機能がテンコ盛りであり、「何がどこにあるのか」を完璧に理解するまでは結構大変である

 

ということは覚えていてほしいと思います。

 

.従って、この原稿を書くためにVisual Studioで表示できるウィンドウやプロパティページを探すだけで、私の「旅の相棒Copilot君にもうお世話になっている始末です。

 

旅はまだまだ続くのであった...

 

ps. 今回C#までやろうかと思いましたが、長くなったので次回。

 

さて、前回装備を確認し、冒険の旅へと出発しました。

 

しかしっ、

 

どんなRPGでも一足飛びに先まで進もうとすると強い敵にやられてお陀仏になってしまいますので、先ずは「出発地周辺(車のナビのようですが)」で経験値を稼ぐことが必要です。

 

C、C++やC#というC調プログラミング言語の出発点

 

といえば、やっぱり

 

Hello World!

 

しかないですよね?

 

ということで、最初は私の選択したプログラミング言語、「C++とC#」のテンプレートでこのメッセージを出力してみましょう。テンプレートは72もありますが、この二つの言語で使えそうなのは、

 

1.コンソール(昔ながらのDOS風の文字列出力です。)

2.シンプルなウィンドウ(「デスクトップ」という呼び方をされているようです。)

 

の二つを取り上げて、C++とC#でプロジェクトを作ってみましょう。題して「Hello World!巡り」です。

 

【予告編】

<コンソール版>

 

 

<デスクトップ版>

 

さーて、HelloWorld!5景。どれが何でしょうか?

 

さて、新たな冒険の旅が開始され、Visaul Studioも正常にダウンロード、インストールされました。(

【Visual Studio探検記】インストールしなければ何も始まらない

 

新たな冒険の旅の出発前に、装備を点検する必要があります。

 

ということで、Visual Studioを立ち上げましょう。

 

【オープニング画面】

 

【開始する】

 

Visual Studioを起動すると、オープニング画面に続き「開始する」というダイアログが開き、左側に既作成のソリューションやプロジェクト)がリストされ、右側に「新しいプロジェクトの作成」、「プロジェクトやソリューションを開く」、「folderを開く」、「リポジトリーのクローン」という選択肢が表示されます

:初めての方には耳慣れない言葉(その説明も「コンテナ」etcと日常の言葉ではありません)ですが、ドラマなどに出てくる反社組織に準えれば「最終的に完成する仕事(「ワシは日本をすべてシマにしちょるけん!」)」という指定暴力団がソリューション、その系列の手下となる構成員組織が「プロジェクト」で「具体的な最終仕事の完成の為の下仕事(「(敵対する組織の)親分のタマとっちゃる」、「(薬とか風俗とかetcで)シノギをする」とか、etcの具体的業務)」を請け負い、指定暴力団の傘下にはいっている、というアナロジーで如何?

 

何もVisual Projectで作っていない段階なので、「新しいプロジェクトの作成」を選択するとそのダイアログが表示されます。

 

【新しいプロジェクトの作成】

 

ここで先ず、(私が選択した)C++とC#は良いとして、それ以外に(選択した覚えのない)VB、F#やその他も含み(私が最も恐れ、嫌う)

 

72のテンプレート(そんなにいらんがな...)

 

が右側にリストされます。その内容も(自分で選んだC++とかC#とかの)プログラミング言語は良いとして、ワークロード)、プラットフォーム(大体「プログラムが動作するOS」と「PCとかスマホとかのハード」と理解しておれば困らないでしょう)等を含め、「クラスライブラリ」「ワーカーサービス」「テストプロジェクト」「NUnit」「ATL」etcやたらカタカナ、英文省略語が多く初心者方には非常に分かり辛いものになっています。

:「Visual Studioで特定のプログラミング言語やプラットフォームに必要なツールや機能をまとめたセット」だそうです。

 

要らないものは排除したり、消去したいと思うのですが、webに書かれたVisual Studio 2022用の方法ではうまくいきません。下手にいじくっておかしくなるのも心外なので、仕方なくこのまま付き合うとしましょう。(尚、その際は↑のイメージの赤枠で囲った)「テンプレートの検索」や「言語(ドロップダウンリスト)」により絞り込みをかけることをお勧めします。

 

要すればこの困惑、当惑は「大都市の一流レストランへ行き、分厚いメニューとワインリストが渡されて、中身は全く知らない、読めない、分からない料理名、酒名で満ち溢れている状態」で、

 

早くなんかオーダーしろ、というプレッシャー

 

がかけられている時に感じるものと類似していることに気付かされるでしょう。

 

5年前はアセアセ、セッセとGoogleでインターネット上の情報を漁って自分で調べましたが、5年後の今は

 

ねぇ、Copilotぉ、ここんとこ分かんない。教せーて!

 

といえば懇切丁寧に教えてくれる時代になったというところだけが違うのです。

 

MicrosoftのCopilotが英雄の仲間になりました(♩~♪~♬~♫~)

┐(´∀`)┌ヤレヤレ

 

やっと始まったこのシリーズ、続けるにはタイトル通り「インストールしなければ何も始まらない」。

 

ということで、

 

今回はVisual Studio 2026のダウンロードとインストールをやりましょう。

 

1.Visual Studio 2026のダウンロード

前回もリンクを載せていましたが、最新のVisual Studio 2026はここからダウンロードします。(個人の方のプライベート使用であれば、フリーダウンロードのCommunity版を選んでください。)

 

 

2.Visual Studio 2026のインストール

とりあえずポチっとすると、本体ではなく

 

 

インストーラー(VIsual Studio Setup.exe)」というものがダウンロードされます。

ここでも迷わず起動しましょう。すると、

 

 

が出ますので構わず「続行」してください。

 

さて、次が鬼門です。

 

 

ユーザーが欲しい「ワークロード」を選択し、既定のコンポーネントにアラカルトを加えるか「個別のコンポーネント」で選択し、使用する言語()に対応する「言語パック」、そして最後に「インストールの場所」も選択できます。

:既定は「OSと同じ言語(日本語)」ですが、英語や欧州圏、アジア圏の適用を考えるなら言語パックをダウンロード、インストールできます。

 

がっ、

 

初めて「プログラミングをしてみよう」と考えた初心者は、「ASP.NETとWeb開発」、「AzureとAIの開発」を読んだあたりで、

 

???なんのこっちゃ???

 

となるか、

 

^^^ビビってしまう^^^

 

のではないでしょうか?次の「Python開発」あたりで、

 

あっ、聞いたことがあるっ!

 

位で、Windowsの主流となった「.NET」も

 

それってプログラミング言語?

 

となったり、「マルチプラットフォーム開発」「デスクトップ開発」もまだ用語に馴染みがないのではないでしょうか?

 

折角プログラミングに興味を持ったのに、門前で怖がらせて引き返したり、挫折感を味わう初心者の方がいないでしょうか?心配です。

 

かくいう(すでに初心者ではない)私もこの「プログラミング環境インストール用タブ選択」を前にして

 

ウ~ン

 

と唸り、結局「C調老人」として

 

 

C#用に(右の詳細でC#関連のみを選び).NETデスクトップ開発」と「C++、Cによるデスクトップ開発」のみをインストールすることにしました。

 

さーて、どうなったでしょうか?

 

まぁ、こういう経緯で始めたこの企画ですが、多くの方に面白く読んでいただけるよう、

 

1.RPG(注)ゲーム仕立て

2.プログラミングのド初心者、趣味プロ(経験者)いずれもの目線

 

で進めて行ければと考えております。

:近時の軍事緊張が高まる施錠とはいえ、こっちではありません。こっちです。

 

まず、

 

Visual Studioって、何?

 

から始めます。Visual Studioはご本家Microsoftが始めた本格的なマルチ言語、マルチプラットフォームのIDEIntegrated Development Environment - 統合開発環境)です。(正しいよね?Wikiさん)"Visual Studio"という名称は1997年からあったらしいのですが、「統合環境」的なありがたみが薄かった(そしてべらぼうに高かった - 2桁万円)ので、大昔私もコードプログラミングのMFCのVisual C++ 6.0とWYSWYGプログラミングのVisual Basic 6.0を単体で(大学生だった息子にAcademic版を2 x 2 = 4万円程度で買わせ)使ったことがあります。、MFCの苦行を強いるプログラミングと当時のVisual Basic言語になじめず、ほとんど使わないでお蔵入りとなりました。

この(Ver 6.0)後、Microsoftは(.NETの始まりとともに)方針の大変換を行い、すべての開発環境を統合したVisual Studioを2002年に出し、以降技術的な変遷とともに構成やバージョンを重ねて現在に至っております。なお、(↑のサイトにも書かれていませんでしたが)Microsoftが買収したGitHubも組み込んで、バージョン管理のみならずオープンソースや協働態勢が大幅に進化したことも付言しておきましょう。(Community版のリターンとして膨大なソフト資産を形成することができた、とも言えます。)

 

次に、

 

 

Visual Studioって、誰でも使えるの?

 

ですが、Community Editionは(過去あんなに暴利を暴虐にむさぼっておきながら-クソッ)個人または小規模組織であれば完全無料で提供されております。

 

先ずは「見る前に跳べ」「百聞は一見に如かず」「物は試し」「実践躬行(じっせんきゅうこう)」「当たって砕けろ」ということで、お時間とCPU能力とディスクスペースに余裕のある方

 

Visual Studio 2026ダウンロード(Community版-無料ダウンロード)

 

から最新版をダウンロードしてみてください。(

:実はかくいう私も完全定年となった2020年(コロナパンデミック勃発の年でもありますが)にEmbarcaderoからC++ Builder(1年間のトライアル版)とMicrosoftからVisual Studio 2019をダウンロードして「20年ぶりのモダンプログラミング環境」と向き合いましたが、当時の私の(プログラミング力量が低かったことに加え)PCがCore - i7とは言え4 Coreで遅かったので(特にエディターの多機能が災いした遅さに)耐え切れず、20年前の自分自身の開発環境(BCCForm and BCCSkelton)に戻ったというのがこのブログの誕生物語です。

 

5年ぶりにMicrosoftのIDEがどのようになっているのか?

 

を探検してゆくことにします。

 

この間書いた「無駄話」で「【Visual Studio探検記】の準備が整いました」と書き、現実にそのブログインスタンス(ブログクラスの実体-要すればネタ)を作っていますが、ネタ的にC++やC#のプログラムが要るので、先ずは先般Embarcaderoの32bitネイティブコンパイラーで作ったC++用の「各種ソートの比較」をベンチマークプログラム的に使おうかと考えました。

 

"Comparison_Sort.cpp"というプログラムなんですが、まだ覚えておられるでしょうか?

 

 

となると、

 

矢張りC#用にも欲しいですよね?と、いうことでこのプログラムをC#に移植(末尾参照)し、実行してみました。

 

解説:一番遅いBubbleソートはC++よりも遅いが、アルゴリズム的に速いHeap、Quick、Shellソートでは(テストプログラムのデータセットの違いを考えても)早くなっていることがわかります。なお、.NETは.Netはコンパイラーとインタープリターの両方の特性を組み合わせたような実行環境をとっており、「遅い」とか「速い」とかいろいろと言われますが、実際に計測すると面白いですね。

 

ウ~ン、.NET、速いですね。恐るべし。

アルゴリズムの勝利、恐るべし。

 

【Compare_Sort.cs】

解説:以下のプログラムは、C++用のComparison_Sort.cppのアルゴリズム、構成をなるべく忠実にC#に移したものですが、両言語の違いにより変更したところがあります。

///////////////////////

// C# Sort Comparison
///////////////////////

using System;
using System.Diagnostics;        //Stopwatchを使う為

namespace Comparison_Sort
{
    class comparison_sort
    {
        ///////////
        //定数定義
        ///////////

        const int MAX = 10000;        //取り敢えず10,000がお勧め
        ////////////////////
        // 共通テスト用配列
        ////////////////////

        int[] original = new int[MAX];
        double[,] results = new double[4, MAX];

        ///////////////
        //Mainメソッド
        ///////////////
        public static void Main()

        {
            comparison_sort cs = new comparison_sort();
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for(int i = 0; i < MAX; i++) {
                cs.comparison(i);
            }
            for(int i = 0; i < 4; i++) {
                cs.print_result(i);
            }
            sw.Stop();
            TimeSpan ts = sw.Elapsed;    //時間ts.Hours, 分ts.Minutes, 秒ts.Seconds, ミリ秒ts.Milliseconds
            string elapsed = String.Format("Total time consumed for the above processing is: {0:00.000000}", ts.Milliseconds);
            Console.WriteLine(elapsed);
            Console.WriteLine("Push CR key ..." + Environment.NewLine);
            Console.Read();        //コンソールを閉じない為
        }

        ///////////////
        // Comparison
        ///////////////

        void comparison(int n)
        {
            //乱数の初期化
            Random rand = new Random((int) DateTime.Now.Ticks & 0x0000FFFF);
            //テスト用の配列の作成
            //解説:C#では引数に配列を渡す場合、参照渡しになる-https://water2litter.net/rye/post/c_array_ref/
            int[] test_array = new int[MAX];    //テスト用ローカル変数
            for(int i = 0; i < MAX; i++)
            {
                test_array[i] =                 //originalを保存し、テストはこれで行う
                original[i] = rand.Next();        //0から32,767の整数で初期化する
            }
            //時間計測の準備
            Stopwatch sw = new Stopwatch();
            //ソート比較
            sw.Start();
            bubblesort(test_array);
            sw.Stop();
            TimeSpan ts = sw.Elapsed;    //時間ts.Hours, 分ts.Minutes, 秒ts.Seconds, ミリ秒ts.Milliseconds
            results[0, n] =  (double)ts.Milliseconds;

            for(int i = 0; i < MAX; i++)
                test_array[i] = original[i];
            sw.Reset();
            sw.Start();
            heapsort(test_array);
            sw.Stop();
            ts = sw.Elapsed;    //時間ts.Hours, 分ts.Minutes, 秒ts.Seconds, ミリ秒ts.Milliseconds
            results[1, n] =  (double)ts.Milliseconds;

            for(int i = 0; i < MAX; i++)
                test_array[i] = original[i];
            sw.Reset();
            sw.Start();
            quicksort(test_array, 0, MAX - 1);    //配列、開始添字、終了添字
            sw.Stop();
            ts = sw.Elapsed;    //時間ts.Hours, 分ts.Minutes, 秒ts.Seconds, ミリ秒ts.Milliseconds
            results[2, n] =  (double)ts.Milliseconds;

            for(int i = 0; i < MAX; i++)
                test_array[i] = original[i];
            sw.Reset();
            sw.Start();
            shellsort(test_array);
            sw.Stop();
            ts = sw.Elapsed;    //時間ts.Hours, 分ts.Minutes, 秒ts.Seconds, ミリ秒ts.Milliseconds
            results[3, n] =  (double)ts.Milliseconds;
        }

        ////////////////
        // Bubble Sort
        ////////////////
        /*
            n個の整数配列dataを総て順に比較、交換し、配列の最後から大きい順に並べる
            通称「馬鹿ソー
ト」
        */
        void bubblesort(int[] data)
        {
            int i, j, temp, n = data.Length;

            for(i = n - 1; i > 0; i--) {        //配列の末尾から先頭へ
                for(j = 0; j < i; j++) {        //配列の先頭から末尾へ
                    if(data[j] > data[j + 1]) {    //次の配列要素の方が小さければ交換する
                        temp = data[j];
                        data[j] = data[j + 1];
                        data[j + 1] = temp;
                    }
                }                                //その結果iに最大の要素が置かれる
            }
        }

        //////////////
        // Heap Sort
        //////////////
        /*
        ヒープソートは次のようなヒープ(二分木)の「親-子」関係を用いる
                Parent(0)
                  /\
          ChildL(1)  ChildR(2)

        この二分木を用いて配列を表現すると以下のようになる(数字は添字)
                   0
                 / \
                1     2
              / \ / \
             3    4 5    6
           / \
          7     8 ...
        */


        void exchange(int top, int[] data, int n)
        {
            int temp = data[top];                        //開始時の二分木の親の値をtempに退避
            //子のある二分木の左の子供を指定し、(交換があればやり直す為)末尾迄子孫を辿る
            for(int i = top * 2 + 1; i < n; top = i, i = top * 2 + 1) {
                if(i < n - 1 && data[i] < data[i + 1])    //右の子供が左の子供より大きければ
                    i++;                                //右の子供を指定する
                if(temp >= data[i])                        //開始時の親と大きい方の子供を比較
                    break;                                //親が大きければループを抜ける(topは変化しない)
                data[top] = data[i];                    //二分木の親に大きい子の値を代入する
            }                                            //大きい子(data[i])はループのi = topで
            data[top] = temp;                            //data[top]となり、退避した親の値を代入
        }

        void heapsort(int[] data)
        {
            int top, i, n = data.Length;
            for(top = n / 2 - 1; top >= 0; top--)        //最初に「子がある最後の二分木」から
                exchange(top, data, n);                    //「親が最大値を持つ」ように頂点まで継続する
            for(i = n - 1; i > 0; i--) {                //次に配列の最後と頂点(最大値)の値を交換し
                int temp;
                temp = data[i];
                data[i] = data[0];
                data[0] = temp;
                exchange(0, data, i);
            }                                            //配列の最後を切り離し、その前の要素から頂点まで継続する
        }

        //////////////
        // Quick Sort
        //////////////
        //検索範囲(startとend)で、基準値(key)を定め、それよりも多いものと少ないものを分割し、更にkeyの前後で同じ処理を再帰呼び出しする。

        void quicksort(int[] data, int start, int end)    //配列、開始添字、終了添字
        {
            if(start < end)                                //開始位置が終了位置以上で終了(最後の再帰呼び出し参照)
            {
                int left, right, key, kp;                //順に右進行添字、左進行添字、「分水嶺」値、その添字
                //右端乃至左端に到達し、breakで抜けるまでループする
                for(left = start, right = end, key = data[right];;) {
                    while(key >= data[left])
                    {
                        left++;                            //keyを超える値迄右に進む
                        if(left == right)                //配列添字が範囲を超えないよう追加
                            break;
                    }
                    if(left < right)                    //rightの位置迄は
                        data[right] = data[left];        //leftの値をrightの位置に書き込む
                    else                                //leftがright位置を過ぎたなら
                    {
                        kp = right;                        //keyの位置をrightの位置としてループを抜ける
                        break;
                    }
                    while(key <= data[right])
                    {
                        right--;                        //key未満の値迄右に進む
                        if(right == 0)                    //配列添字が範囲を超えないよう追加
                            break;
                    }
                    if(left < right)                    //leftの位置迄は
                        data[left] = data[right];        //rightの値をleftの位置に書き込む
                    else                                //rightがleft位置を過ぎたなら
                    {
                        kp = left;                        //keyの位置をleftの位置としてループを抜ける
                        break;
                    }
                }
                data[kp] = key;                            //keyポインターの位置にkeyの値を代入する
                quicksort(data, start, kp - 1);            //開始位置からkeyポインター迄で再帰呼び出しする
                quicksort(data, kp + 1, end);            //keyポインターから終了位置迄で再帰呼び出しする
            }
        }

        //////////////
        // Shell Sort
        //////////////
        //シェルソートは配列要素を一定の距離を置いたグループに分けて比較、交換を行いソートする方法

        void shellsort(int[] data)
        {
            int gap, i, j, temp, n = data.Length;        //gapは比較、交換する要素間の距離
            for(gap = n / 2; gap > 0; gap /= 2) {    //初期的な距離は配列サイズの1/2とする
                for(i = gap; i < n; i++) {            //距離は徐々に狭まってゆく
                    for(j = i - gap; j >= 0 && data[j] > data[j + gap]; j -= gap) {
                        //v[j]とv[j + gap]を交換する
                        temp = data[j];
                        data[j] = data[j + gap];
                        data[j + gap] = temp;
                    }
                }
            }
        }

        /////////////////
        // Print results
        /////////////////

        void print_result(int n)
        {
            double[] array = new double[MAX];    //n行の配列のコピー用
            string[] title = new string[4] {">>> Bubble Sort <<<", ">>> Heap Sort <<<", ">>> Quick Sort <<<", ">>> Shell Sort <<<"};
            double max = 0.0, min = (double)MAX, med = 0, ave = 0;
            for(int i = 0; i < MAX; i++)
            {
                if(results[n, i] > max)            //n番目のソートの結果の最大値を求める
                    max = results[n, i];    
                if(results[n, i] < min)            //n番目のソートの結果の最小値を求める
                    min = results[n, i];
                ave += results[n, i];            //n番目のソートの結果を合計する
                array[i] = results[n, i];        //n番目の配列をコピーする
            }
            Array.Sort(array);                    //コピー配列をソートする (
            med = array[(MAX % 2 != 0) ? MAX / 2 : MAX / 2 - 1];
            Console.WriteLine(title[n]);
            Console.WriteLine("最大値  :{0:00.000000}", max);
            Console.WriteLine("最小値  :{0:00.000000}", min);
            Console.WriteLine("平均値  :{0:00.000000}",  ave / MAX);
            Console.WriteLine("中央値  :{0:00.000000}", med);
            Console.WriteLine("総所要時間:{0:00.000000}", ave);
            Console.WriteLine();
        }
    }
}

以下はCopilotのアドバイスです。

.NETArray.Sort()メソッドは、状況に応じて**イントロソート (Introsort)**という複合アルゴリズムを使用しています。

具体的には、以下の3つのソートアルゴリズムを組み合わせて、効率的なソートを実現しています。

  • クイックソート (Quicksort): 一般的なケースで使用されます。平均的な時間計算量はO(n log n)と高速です。
  • 挿入ソート (Insertion Sort): 配列の要素数が非常に少ない場合(通常16要素以下)に使用されます。オーバーヘッドが少なく、小さな配列では効率的です。
  • ヒープソート (Heapsort): クイックソートの再帰が深くなりすぎた場合(最悪ケースに陥りそうな場合)に切り替わります。これにより、最悪ケースでの時間計算量がO(n^2)になるのを防ぎ、常にO(n log n)のパフォーマンスを保証します。

これにより、どのようなデータに対しても高いパフォーマンスと安定した時間計算量を実現しています。

なお、.NET Frameworkのバージョン4.5より前では、デフォルトのアルゴリズムは単純なクイックソートでした。 

 

こういう「遊び」って、面白くありません?