20201023のC#に関する記事は5件です。

ツールバーに追加されたボタンのスタイル(ToolBarButtonStyle)を明示的に設定する方法

はじめに

本記事はWPFでツールバーに追加したボタンに自動的に設定されるスタイル(ToolBarButtonStyle)を明示的に設定する方法をまとめます。

設定方法

ツールバーに追加したボタンにはToolBarButtonStyleが自動的に設定されます。以下のようなスタイルをボタンに設定することでToolBarButtonStyleを明示的に設定できます。

<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"/>

詳細はこちらの記事を参考にしてください。

まとめ

ツールバーに追加したボタンに自動的に設定されるToolBarButtonStyleは明示的に設定ができる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ローグライクRPG開発日記_0

かねてより制作してみたかったローグライクRPGに着手しようと思い、今、こうして書かせてもらっています。
自分の備忘録のように書き込んでいますので、多少読みづらいのはご了承ください。
とりあえず、色々メモしておきます。

<メイン>
・「風来のシレン」のような2D見下ろし型
・ただし、レベルは1には戻らない(なぜこのようにするのかは後述、一応Lv1ダンジョンも別途作りたい)
・主人公候補は3人、その中から一人を選んでゲームスタート(周回要素あり)
・主人公それぞれにストーリーが有り、ダンジョンクリアによって進行
・主人公たちの拠点となる町には様々な施設があり、施設ごとに恩恵がある
(素材やお金を寄付することで施設レベルが上がり、より良いサービスを受けることができる)
[施設例]
○商店…アイテム売買
○酒場…依頼受領、情報収集
○鍛冶屋…武器の強化、売買
○倉庫…アイテムの保管
○道場…スキルの習得

・ダンジョンは他の冒険者も探索しており、誰かがダンジョンを踏破すると、そのダンジョンの情報が酒場で売られるようになる。
・他の冒険者が最終ダンジョンを踏破してしまうとゲームオーバー(実質的な時間制限)
 LVや倉庫にあるアイテムを保持したまま周回する事もできる

<要素>
・アイテム、敵の図鑑機能(アイテムは入手、敵は倒すことで図鑑に登録される)
(項目ごとに主人公たちのコメントを入れれたらなあ)
・依頼(アイテム納品、敵の討伐など。クリアすると報酬がもらえる)
・モンスターハウス
・施設レベルアップの素材(敵、又は床から入手可)

実装したいと思っているのはこの辺りです。施設育成に重きを置きたいので、いかにリソースを効率よく集め、施設の人たちに貢ぎ物をし、他の冒険者を出し抜いてダンジョン最深部にある宝を入手できるか、をコンセプトとして制作しようと思っています。

<懸念事項>
・Unityの知識不足(まだまだわからないことが多い)
・広告、販売の仕方
・モチベーション
・制作期間の不明瞭さ(何ヶ月かかるかわからない)
・イラスト

中々考えることが多いです。特に、イラストに関しては全くのド素人で、頭の中で想像はできてもアウトプットが出来ないもどかしさがあります。なんとか描けるように努力するか(正直どれくらいかかるかわからない…)、外注で探すか(相場がどんなものか不安)、どちらにしようか悩んでいる状態です。

こんな感じでなんとも見切り発車的なスタートですが、見守っていただけると幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity HDRPで透過スクショを撮る

こんにちはっ?八ツ橋まろんです

今回はUnityのHDRPで透過のスクリーンショットを撮る方法とスクリプトを紹介していきます。HDRPでより綺麗な立ち絵を撮りたいVtuberなどに有効です(超限定的)
※以前『UnityのGame画面をスクショするスクリプト』を紹介しましたが、Unityのバージョンによっては再生中でないと使えないことがあったので、今回は再生中でなくとも使えるように改良しています。よってスクリプトは上記記事の上位互換です。(スクリプトはHDRPでなくとも使えます)

HDRPって何?って方はこの単語で検索してみてください。たくさんの方が記事を書いてくださっています。乱暴に説明すると、
『高品質なライティングができる、より美しいUnity』
です。

ただし、
『PCの負荷が増えます、つよつよPCを使いましょう』
『従来のshaderはほとんどが使えません。HDRP専用のshaderが用意されているので、それを使う必要があります』

HDRPはUnityHubでプロジェクトを作成する際にここ↓↓↓↓を選択することで使用可能です
image.png

HDRP環境の基本的な構築はこの記事では解説しませんので、基礎的な部分は他の記事を参照してください。

環境

Windows10
Unity 2019.4.4f
HDRP 7.3.1

必要な設定

・HDRPで透過背景を扱うには、HDRenderPipelineAssetのColor Buffer Formatを R16G16B16A16にする必要があります(デフォルトではR11G11B10となっていてA成分が無い)
image.png

・Volumeの設定のうち、BloomとVignetteを使っている場合はオフにしておきましょう(どちらも画面全体に適用されるエフェクトなので、透過画像全体に効果が残るため)

・Volumeの設定のうち、Fogを使っている場合はオフにしておきましょう(透過せず背景として映り込んでしまうため)

・Cameraの設定のうち、Background TypeはColorにしておきましょう(Skyだと透過せず背景として映り込んでしまうため)

撮影方法

・以下のスクリプトを空のGameObjectにアタッチし、コンポーネントの右上のアイコンを開いてScreenShotを選択するとGame画面に映っている画面が透過pngとしてResourcesフォルダに保存されます。(Game画面が見えない状態では保存されません)

image.png

ScreenShot.cs
using System;
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class ScreenShot : MonoBehaviour
{
    public bool Alpha = true;
    public string Folder = "Assets/Resources";

    IEnumerator CaptureWithAlpha()
    {
        yield return new WaitForEndOfFrame();

        var rtex = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Default);
        //var tex = ScreenCapture.CaptureScreenshotAsTexture();
        ScreenCapture.CaptureScreenshotIntoRenderTexture(rtex);

        var width = rtex.width;
        var height = rtex.height;
        var texNoAlpha = new Texture2D(width, height, TextureFormat.RGB24, false);
        var texAlpha = new Texture2D(width, height, TextureFormat.ARGB32, false);

        if (Alpha)
        {
            // Read screen contents into the texture
            texAlpha.ReadPixels(new Rect(0, 0, width, height), 0, 0);
            texAlpha.Apply();
        }
        else
        {
            // Read screen contents into the texture
            texNoAlpha.ReadPixels(new Rect(0, 0, width, height), 0, 0);
            texNoAlpha.Apply();
        }

#if UNITY_EDITOR
        SafeCreateDirectory(Folder);
#endif
        // Encode texture into PNG
        var bytes = texAlpha.EncodeToPNG();
        if (!Alpha)
        {
             bytes = texNoAlpha.EncodeToPNG();
        }
        DestroyImmediate(rtex);
        File.WriteAllBytes(Folder + "/" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".png", bytes);
        AssetDatabase.Refresh();
    }
    public static DirectoryInfo SafeCreateDirectory(string path)
    {
        return Directory.Exists(path) ? null : Directory.CreateDirectory(path);
    }
    [ContextMenu("Screenshot")]
    public void Screenshot()
    {
        StartCoroutine(CaptureWithAlpha());
    }
}

以下、補足です。
・InspectorのAlphaのチェックを外すと透過なしのpngになります

・pngの解像度はGame画面の画面設定と同じになります

・Resourcesフォルダが無い場合は自動で作成されます

・保存フォルダを変えたい場合はInspectorのFolderを適当に変えてください

以上になります。快適なHDRPライフを~?
999999.png

八ツ橋まろん

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ソートアルゴリズムを可視化する(コンソールアプリの作成)

0. まえがき

Youtubeで"sort algorithms visualized"などと検索すると、
ソートの様子を描いた綺麗なアニメーション動画がヒットします。
今回は、それを真似てコンソールでソートの様子が見えるプログラムを作りたいと思います。
言語はC#ですが、固有の機能(WPFでデータバインドが~とか)は使用せず、
枠組みからムニムニ作りますのでオブジェクト指向型言語であれば同じように作れるかと思います。

端的には、コンソールで動くこれ(↓)を作ります(バブルソートしてますね)
SortAnimation.gif

1. 開発環境

OS
Windows 10 Home
IDE
Visual Studio 2019 Community
言語
C# 7.0以上 (System.ValueTapleが使えるやつ)

2. 設計(雑)

ソート処理にて要素の交換が発生した時、その時点の配列の内容を画面に表示、
また要素の交換が発生したら画面をクリアして再び配列の内容を表示・・・。
という処理をソート完了まで繰り返していけば上にあるようなアニメが出来そうです。

・・・こう書いてみると、ソートアルゴリズムの中に画面表示の処理を組み込んだら
それで一発完成しそうな雰囲気ですが、折角なのでオブジェクト指向の考え方で作っていきたいと思います。
(何が折角なのかは不明)

2.1. Observerパターンを検討してみる

2.1.1 そもそも?ってなに?

これっす?
https://ja.wikipedia.org/wiki/Observer_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3
またはQiitaで検索検索ゥ!!諸先輩方の大変分かりやすい記事がヒットしますヨ。

2.1.2 パターンに当てはめてみる

上のリンク内の説明に従うと、
ソートする人 → 通知する側(配列内容を交換する度にその状態をお知らせ)
画面表示する人 → 通知を受ける側(お知らせ内容に従い画面表示)
と置けそうです。

2.2 クラス図に起こしてみる

使用したツール
まゐくろそふと ゑくせる

左側が通知を受ける側、右側が通知する側になります。
また水色が根幹の仕掛けとなる枠組み(フレームワーク部)で、
黄色が具体的な処理を書く部分(アプリケーション部)になります。
こうして俯瞰的に見てみると、通知する側・される側が相互参照していて
こんがらがりそうなんですがそれは・・・。
まぁ、フレームワーク部から順次作っていきます。。。
各クラスの説明は次章にて♨
ClassDiagram.png
クラス図初めて書いたんだけど合ってるんかなこれ

3. 実装

3.0 前準備

System.ValueTapuleを使用可能にする
すみません。冒頭でC#固有の機能は使わないと書きましたが微妙にウソつきました(ヘケッ!)
何かというと、ソートなので要素の交換は必須処理なのですが、
なぜか.NETの標準にはSwap<T>( ref T a, ref T b )のようなメソッドがなく、
自前で作ったことがある方も多いかと思います。
がしかし、C#7.0から使用可能になったSystem.ValueTapleを使えば
Swap(ryを自前で用意しなくても簡単に要素の交換ができるので、
今回はこれを使って下のように書きたいと思います。

マジすげー.cs
( a, b ) = ( b, a );  // こんな風に値の交換を書ける

使用するためにはNugetからゲトってインストールする必要があります。
Nuget.png

画面表示用のメソッドを作成する
Observerパターンとは直接関係ないので切り離してここに記載しました。
アニメーションでの一コマ相当を画面に表示する静的クラス&メソッドです。
一旦画面をクリアしてから描きたいものを表示、それからちょっとタイム。
以降も呼び出される度にクリア→表示→タイムでアニメの再現・・・ってとこです。
タイムを設けているのは、これが無いと目視不可能な程の爆速でアニメが終了し
ポカーン( ゚д゚)となってしまうからです。

Animator.cs
using System.Threading;

namespace SortVisualizerCUI.Application
{
    /// <summary>
    /// コンソールに対しアニメ表示を行う人
    /// </summary>
    public static class Animator
    {
        private const int WaitTime_ms = 100;    // 画面の表示更新の間隔[ms]

        /// <summary>
        /// アニメでいうところの一コマ分を表示する
        /// </summary>
        public static void DisplaySingleFrame( string value )
        {
            Console.Clear();
            Console.WriteLine( value );

            Thread.Sleep( WaitTime_ms );
        }
    }
}

3.1 枠組みを作る(フレームワーク部の作成)

クラス同士の関係を分かりやすくするため共通の絵文字をタイトルに付けました。
同じ絵文字がついているものは派生元・派生先同士、
インターフェースの定義・実装同士ってことです。
:speech_balloon: : 通知する側
:open_mouth: : 通知を受ける側

3.1.1 :speech_balloon: Observableクラス(通知する側)

通知する側が備えておくべき機能を持たせた抽象クラスです。
ここから具象クラスをニョキニョキ派生させてそこへやりたい処理を書きます。
通知を受ける側への参照をリスト(observers)で複数持っているのは、
例えばGUIなら複数のコントロールに一斉に状態変更を知らせたい時があるためです。
今回は通知する相手が一人なので死に機能なんですけどね(ヘケッ!)
細けぇこたぁ(ryソースコード中のコメントをご参照くださいませ。m(_ _)m

Observable.cs
using System.Collections.Generic;

namespace SortVisualizerCUI.Framework
{
    /// <summary>
    /// 通知する側の抽象クラス
    /// </summary>
    public abstract class Observable
    {
        /// <summary>
        ///  通知を受ける側への参照。
        /// </summary>
        private readonly List<IObserver> observers = new List<IObserver>();

        /// <summary>
        ///  通知を受ける側を追加する。
        /// </summary>
        /// <param name="observer"></param>
        public void AddObserver( IObserver observer ) => observers.Add( observer );

        /// <summary>
        ///  通知を受ける側を削除する。
        /// </summary>
        /// <param name="observer"></param>
        public void RemoveObserver( IObserver observer ) => observers.Remove( observer );

        /// <summary>
        ///  通知を受ける側へ自身の状態変更を知らせる。
        /// </summary>
        protected void NotifyObservers() => observers.ForEach( observer => observer?.Update( this ) );
    }
}

3.1.2 :open_mouth: IObserverインターフェース(通知を受ける側)

インターフェースの定義なのでこれだけです。
読んでも「で?」って感じなので次ィ・・・。

IObserver.cs
namespace SortVisualizerCUI.Framework
{
    /// <summary>
    /// 通知を受ける側のインターフェース定義
    /// </summary>
    public interface IObserver
    {
        /// <summary>
        ///  通知する側から状態変更の知らせを受けた時の更新処理
        /// </summary>
        /// <param name="observable"></param>
        void Update( Observable observable );
    }
}

3.2 具体的な処理を書く(アプリケーション部の作成)

3.2.1 :speech_balloon: SortExecutableクラス(通知する側:抽象)

すみません、また微妙にウソついてます(ヘケッ!)具体的な処理を書くということで、
?のObservableクラス(抽象)から直接バブルソートにあたるクラス(具象)を
派生させてもよかったのですが、例えばXXXソートを増やしたい場合、
同じ処理を何度も書くのはダルいし読んでいてうざいので共通化出来るプロパティ・
メソッドを抽出しここに抽象クラスとしてまとめました。
(抽象クラスにしたのはこいつをnew()!!でインスタンス化されてもしょーもない為)

ただ、単に処理を共通化したいからという理由で上位クラスに抽出し派生させるのは
良くない説もあり(is-a関係がとれているか?など継承は本当に難しい)、
この時点でほのかに糞の香りがしていますが、かといって
合成で実現するのも違和感があるためもうこれで押し通します。
これにて通知する側は三段階派生することになりました。

Observableクラス(抽象)
  ↓
SortExecutableクラス(抽象)←今ココ
  ↓
XXXSortExecutableクラス(具象)

んでソースコードです。例により細けぇこたぁ(ryコメントで書いていますが、
肝はItemsプロパティのSetにてNotifyObservers()を呼んでいる箇所で、
これが自分自身の状態変更を知らせる=通知を受け取る側に仕事をさせるトリガーです。
NotifyObservers()内で登録している全IObserverUpdate()(仕事しろ)の指示を出しています。

SortExecutable.cs
using SortVisualizerCUI.Framework;
using System.Collections.Generic;
using System.Linq;

namespace SortVisualizerCUI.Application
{
    /// <summary>
    /// ソート実行体抽象クラス
    /// </summary>
    public abstract class SortExecutable : Observable
    {
        /// <summary>
        /// ソート対象のデータ
        /// </summary>
        protected int[] items;

        /// <summary>
        /// コンストラクタ。ソート対象のデータで初期化する。
        /// </summary>
        /// <param name="items"></param>
        protected SortExecutable( IEnumerable<int> items )
        {
            this.items = items.ToArray();
        }

        /// <summary>
        /// ソート対象のデータ。
        /// Setされるたびに更新した内容を通知を受ける側へ知らせる。
        /// </summary>
        public IReadOnlyCollection<int> Items
        {
            get => items;
            set
            {
                if( !value.SequenceEqual( items ) )
                {
                    items = value.ToArray();
                    NotifyObservers(); // ★ココ!で通知を受ける側へ状態変更を知らせる
                }
            }
        }

        /// <summary>
        /// 実際のソート処理。派生先クラスで各アルゴリズムの実装を強制させるため抽象メソッドとしている。
        /// このメソッド内で要素を交換した場合、必ずItemsプロパティを設定することにより通知を受ける側へ状態変更を知らせること。
        /// </summary>
        public abstract void Sort();
    }
}

3.2.2 :speech_balloon: BubbleSortExecutable(通知する側:具象)

これが最終派生クラスになります。通知する仕組みは派生元に定義済みなので、
あとはSort()メソッド内にバブルソートのアルゴリズムを書くだけでおk。
要素の交換が発生した直後にItemプロパティを更新することにより交換直後の配列の状態を
通知を受け取る側へお知らせしています。
★ココ!で通知を受ける側へ状態変更を知らせるのコメントの箇所)

BubbleSortExecutable.cs
using System.Collections.Generic;

namespace SortVisualizerCUI.Application
{
    /// <summary>
    ///  バブルソート実行体
    /// </summary>
    public class BubbleSortExecutable : SortExecutable
    {
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="items"></param>
        public BubbleSortExecutable( IEnumerable<int> items ) : base( items )
        {
        }

        /// <summary>
        /// ソートの実行。要素を入れ替えるたびに通知を受ける側へ状態変更を知らせる。
        /// </summary>
        public override void Sort()
        {
            var array = items.Clone() as int[];
            for( int i = 0; i < array.Length - 1; i++ )
            {
                for( int j = array.Length - 1; i < j; j-- )
                {
                    if( array[j] < array[j - 1] )
                    {
                        // System.ValueTapleの機能による要素の交換
                        (array[j], array[j - 1]) = (array[j - 1], array[j]);

                        // ★ココ!で通知を受ける側へ状態変更を知らせる
                        Items = array;
                    }
                }
            }
        }
    }
}

3.2.3 :open_mouth: SortObserverクラス(通知を受ける側)

?のBubbleSortExecutableから間接的にUpdate()をぶっ叩かれて
画面表示をするクラスです。
これ自体は大したことをしていないので、通知側する側からの処理の流れをまとめると、

  1. BubbleSortExecutableで配列要素を交換したらItemプロパティを更新。
  2. ItemプロパティのSet内でNotifyObservers()が呼ばれる。
  3. NotifyObservers()内から通知を受ける側のUpdate()を呼ぶ。
  4. 通知を受ける側のUpdate()内で交換済みの配列を画面表示する。(今回は各配列要素の値を棒グラフ状に変換)

…とこんな感じです。各メソッドの定義が色んなファイルに散っているので
パッと見で処理を追いづらいですね。:sweat:

SortObserver.cs
using SortVisualizerCUI.Framework;
using System;
using System.Linq;

namespace SortVisualizerCUI.Application
{
    /// <summary>
    /// ソートの状態変更の通知を受ける側
    /// </summary>
    public class SortObserver : IObserver
    {
        /// <summary>
        /// ソート実行体(通知する側)を設定し、自分自身を登録する。
        /// </summary>
        public SortExecutable DataSource
        {
            set => value.AddObserver( this );
        }

        /// <summary>
        /// ソート実行体から変更通知を受け取った時の更新処理。
        /// </summary>
        /// <param name="observable"></param>
        public void Update( Observable observable )
        {
            if( observable is SortExecutable sortExecutable )
            {
                var str = string.Empty;
                foreach( var n in sortExecutable.Items )
                {
                    // 現在のソートの状態を数値の並び→横棒グラフ状の文字列に変換する。
                    str += new string( Enumerable.Repeat( "■", n ).SelectMany( x => x ).ToArray() )
                        + Environment.NewLine;
                }

                // アニメの一コマ分として画面に表示する
                Animator.DisplaySingleFrame( str );
            }
        }
    }
}

3.3 呼び出し側を作って完成!

複雑な仕掛けは各クラスの裏側に隠れているので、呼び出し側はこんなあっさり風味です。
今回は直接オブジェクトをnew()!!して生成していますが、
通知を受ける側sortObserverDataSourceに通知する側bubbleSortを設定し忘れると、
「何も動かねぇ・・・」状態になるので、一連の生成処理をファクトリメソッドにまとめても良いかもしれません。

Program.cs
using SortVisualizerCUI.Application;
using System;
using System.Linq;

namespace SortVisualizerCUI
{
    internal class Program
    {
        /// <summary>
        /// エントリーポイント
        /// </summary>s
        private static void Main()
        {
            // 1~20の数値を生成・シャッフルし、バブルソートの初期値とする。
            var bubbleSort = new BubbleSortExecutable( 
                                 Enumerable.Range( 1, 20 ).OrderBy( x => Guid.NewGuid() ) );

            // バブルソート実行体を通知を受ける側へ設定する
            var sortObserver = new SortObserver()
            {
                DataSource = bubbleSort
            };

            //  バブルソートの実行。この処理内でソート処理のアニメーションが実行される。
            bubbleSort.Sort();
        }
    }
}

4. 動作確認

「Ctrl」+「F5」で実行すると、冒頭でお見せしたバブルソートのアニメーションが
コンソールに表示されるハズです。
・・・が、けっこうチラつきます。調べたところConsole.ほにゃらら()を駆使すれば
回避出来るっぽいのですが、今回はこれで許し亭♨

GitHubにプロジェクトを丸ごと置きました。もしよければ弄ってやって下さい。
SortVisualizer

5. プラスα

〇〇〇ソートを追加する
SortExecutableクラスから派生させた先でソートのアルゴリズムを書くだけで
容易に追加が出来そうです。

GUIアプリとして作る
コンソール表示からフォームへの表示になるため、通知を受け取る側は
ゴミ箱逝き不可避ですが、通知する側は再利用できそうです。
もっとも、C#には変更通知の機能が備わっているためそちらを使うのが正道でしょうが。。。

6. あとがき

記事が無駄に長くなってしまった感が。
今回はぷちプログラムのためやってる内容に対して
ソース全体が大げさになってしまいましたが、
ある程度の規模以上になるとオブジェクト指向の良さが表れてくると思いました。

小生は、実務経験はほぼほぼ素のC言語でやってきたので、
オブジェクト指向の考え方、実装としておかしい点、説明不足などあれば
ご指摘・アドバイス頂けると大変うれしく存じます。

それでは、アディオス!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#_コレクション

どの言語でもあるように、C#でも複数のデータを扱う為の方法が存在します。これを "コレクション" と呼びます。
今回はコレクションについて述べていきます。

コレクション

コレクションにはいくつか種類があります。
どれも複数のデータを扱えるという点で同じではありますが、細かく使い方が違います。

コレクション 特徴
配列(Array) 固定長。インデックスで要素にアクセスできる
リスト(List) 可変長。インデックスで要素にアクセスできる
ディクショナリ(Dictionary) 可変長。キーと値を紐づけてアクセスできる
スタック(Stack) 可変長。先入れ後出し(FILO)式
キュー(Queue) 可変長。先入れ先出し(FIFO)式

これらについて一つずつ見ていきましょう

配列(Array)

最も基本的なコレクションです。
簡単に扱うことができますが、固定長なので必要なデータの要素数が最初から分かっている場合に使うことができます。

Program.cs
int[] arr = new int[3];
arr[0] = 3;
arr[1] = 5;
arr[2] = 7;

C#ではコレクションのインデックス(要素番号)は0から始まるので、要素数3の配列を作成した場合、アクセスできるインデックスは0~2の3つになります。
また、要素外のインデックスにアクセスした場合、実行時エラーが発生します。

配列は次のように最初から要素を指定して初期化することも可能です。

Program.cs
int[] arr = new int[] { 3, 5, 7 };

リスト(List)

配列とは違い、リストは要素数が可変なのでAddメソッドを使うことで要素を追加していくことが可能です。

Program.cs
List<int> list = new List<int>();
list.Add(3);
list.Add(5);

ただし、リストにおいても要素外のインデックスへのアクセスは実行時エラーになります。
※言語によっては初期値で自動追加される場合もありますが、C#のリストはプログラマの想定外の挙動を抑制するためエラーとされています。

また、それを回避するためにある要素がリストに含まれているかどうかを判定するContainsメソッドも持っています。

Program.cs
List<int> list = new List<int>();
list.Add(3);
Console.WriteLine(list.Contains(3));
Console.WriteLine(list[0]);
Console.WriteLine(list.Contains(5));
出力
> True
> 3
> False

ディクショナリ(Dictionary)

キーと値を紐づけた情報を複数持つことができます。他の言語ではハッシュマップなどとも呼ばれます。

ディクショナリは配列やリストの様な"何番目"という考え方ではなく追加された"キー"を基準に値にアクセスしていきます。
キーと値のペアはリストと同じくAddメソッドによる追加、ContainsKeyメソッドによるキーの確認ができます。

Program.cs
// Dictionary<キー型, 値型>
Dictionary<int, string> dic = new Dictionary<int, string>();
dic.Add(2, "Tokyo");
dic.Add(5, "Osaka");

Console.WriteLine(dic[5]);
Console.WriteLine(dic.ContainsKey(2));
Console.WriteLine(dic.ContainsKey(3));
出力
> Osaka
> True
> False

追加されていない要素へアクセスしたり、既に追加されている要素と同じキーを再度追加しようとするとエラーが発生します。
追加されているかどうかの管理を厳密に行うことにより、キーの一意性を保つことができます。

スタック(Stack)とキュー(Queue)

スタックとキューに関しては対比しながら同時に紹介します。

Program.cs
Stack<int> stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
stack.Push(3);
Console.WriteLine(stack.Pop());
Console.WriteLine(stack.Pop());
Console.WriteLine(stack.Pop());
出力
> 3
> 2
> 1
Program.cs
Queue<int> queue = new Queue<int>();
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
Console.WriteLine(queue.Dequeue());
Console.WriteLine(queue.Dequeue());
Console.WriteLine(queue.Dequeue());
出力
> 1
> 2
> 3

上記の通り、スタックやキューは今までのコレクションとは違い、アクセスするときに番号やキーを指定しません。
Push/Enqueueメソッドで要素を追加し、Pop/Dequeueメソッドによって要素を取り出します。

注意しなければならないのはPop/Dequeueメソッドによって取り出した要素はスタックやキュー本体からも消えているということです。

また、スタックとキューで何が最も違うかと言えばその取り出す順番にあります。
スタックは一般にFILO(First In Last Out)方式であり、「最初に入れた要素が最後に出てくる」ことになります。
Popメソッドによって取り出されるのは、要素の中で一番最後にPushされたものです。

反対にキューは一般にFIFO(First In First Out)方式であり、「最初に入れた要素が最初に出てくる」ことになります。
Dequeueメソッドによって取り出されるのは、要素の中で一番最初にEnqueueされたものです。

IEnumerableという概念を知ってからだとコレクションについてのさらに詳しい内容を知ることができますが、このページではここまで。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む