なのログ

だから見てて下さい・・・俺の・・・変身

C#復習篇 vol.5

日が空いてしまいました。
C#のほうもしていきます。こっちはメニューを終えたらなんかしましょうね。
実は今月末に引越をする予定で、今後尚更更新を渋るかもしれなかったりしますが。
やっていきましょう。

ちょっとこんなんに時間割いてらんねえな、作ることしろよ、って思ったので全部やります(唐突)

Append Day1

~コンストラクタとか~

今回からは(C#やる人は)みんな大好きufcppにお世話になります。
前回の終わりに話したとおり、コースだけ前回までのページから引用しつつ、
内容はufcppというかんじです。

そいではやりましょう。

コンストラク

newの時に動くやつですよというはなし。
C#ではこう。

    class Character
    {
        private string Name { get; set; } = "";
        private int AppearancePart { get; set; } = 0;
        // ↓クラスと同じ名前、戻り値無し、引数は設定可
        public Character(string name, int appearancePart) {
            this.Name = name;
            this.AppearancePart = appearancePart;
        }
    }

しれっと前回のコードをそのまま持ってくる胆力。

オーバーロードできるヨ
    class Character
    {
        private string Name { get; set; } = "";
        private int AppearancePart { get; set; } = 0;
        public string VoiceActor { get; set; } = "";

        public Character(string name, int appearancePart) {
            this.Name = name;
            this.AppearancePart = appearancePart;
        }
        // ↓喋るキャラクターも安心(?)むしろ喋らないキャラクター↑も安心?(?)
        public Character(string name, int appearancePart, string voiceActor) {
            this.Name = name;
            this.AppearancePart = appearancePart;
            this.VoiceActor = voiceActor;
        }

引数無しで初期化~みたいなことをさせたほうが適切か・・・?(ufcppはそうしている)
とか思ったものの、宣言のほうで初期化してんな、となったので声優さんに登場頂きました。(余談)

変数初期化子

(使ってしまった・・・)

コンストラクター初期化子

やべえ、知らんかったですこれ。

    class Character
    {
        private string Name { get; set; } = "";
        private int AppearancePart { get; set; } = 0;
 
        public Character()
            :this("誰", 0) {
            Console.WriteLine("ここも動く");
        }

        public Character(string name, int appearancePart) {
            this.Name = name;
            this.AppearancePart = appearancePart;
        }
}

ふむ便利機能ですね。

public Character() :this("誰", 0) {
    // なんかできる
}

一行にも収まるっぽい。
こう色々定義できるとなると気になる件にもキチンと言及してあるのがufcpp岩渕先生です。↓

ちなみに、初期化子とコンストラクターの実行順序は、
変数初期化子 → コンストラクター初期化子 → コンストラクター本体の順になります。
また、変数初期化子は、メンバーの宣言順と同じ順序で呼び出されます。

はえー。
ありがたくも書いてあるのを見たときだいたい上からっぽいですね。(いいのかその認識)

オブジェクト初期化子

コピコピしてしまいますけれども。

Point p = new Point{ X = 0, Y = 1 };

呼ぶ側であとからsetsetしなくていいですよの図。

デストラク

終わるときのやつ。全然使ったことないのでは。

    class Character
    {
        ~Character() {
            Console.WriteLine("受け継がれる意志");
        }
    }

参考:「ジョジョの奇妙な冒険 アイズオブヘブン」,仲間が再起不能になると発動する「受け継がれる意志」システムや登場キャラの情報が公開 - 4Gamer.net
ufcppでも「ちょっとあんまり使われないですね・・・」みたいなことを言われていました。
まあ納得。

Append Day1 演習

    class Point {
        private double x;
        private double y;
        public Point(double x, double y) {
            this.x = x;
            this.y = y;
        }
    }

    class Triangle {
        private Point a;
        private Point b;
        private Point c;
        public Triangle(Point a, Point b, Point c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
    }

か、かんたんだ!


Append Day2

~静的メンバ~

static、ラブです!(脳のメモリが少ないため)

    class MinMax {
        //  最大値の取得
        public static int Max(int n1, int n2) {
            return (n1 > n2) ? n1 : n2;
        }
    }

いいのがあったので、また前回までのやつからもってきました。
「クラスのインスタンスと特有の関わりがないやつ」という感じですかね。

ufcppでは、Personクラスに学名メンバを追加して、
それは個人によらないものなのでstatic属性、としていました。なんだこの賢い例・・・。

まあそう色々言わずとも、
「Mathクラスとかのお家芸やで」という認識でもあります。それでよさそう。(いいのか?)

  • 静的コンストラクタ ・・・というのがあるっぽいですが、存在だけ認識しておきつつ流します。(!?)
静的クラス
    static class MinMax {
        //  最大値の取得
        public static int Max(int n1, int n2) {
            return (n1 > n2) ? n1 : n2;
        }
        public static int Max(int n1, int n2, int n3) {
            return Max(Max(n1, n2), n3);
        }
        //  最大値の取得
        public static int Min(int n1, int n2) {
            return (n1 < n2) ? n1 : n2;
        }
        public static int Min(int n1, int n2, int n3) {
            return Min(Min(n1, n2), n3);
        }
    }

そしてさっきの例に「そういやこれクラスにもstaticつけれるな」となってそのままサンプルに使うスタイル。 こうなると完全に一つの道具として機能して、スバラシイ・・・スバラシイ・・・となるわけですね。
ところでこの例とかは、結局Math的な事しかしていないので、クラスごとstaticにできるというのは、はい、そんなかんじ。

拡張メソッド

あんまり意識してない単語が出てきた・・・ウオオ・・・となっているなのやつさん。

int x = int.Parse("1"); // "1" よりも Parse が前

これに対する

int x = "1".Parse(); // Parse が後に

これっぽい。
「それか~~~」となるなどしましたが、 「それの名前"拡張メソッド"でええんか・・・?」とも思うなど。

定義は

static class Extensions
{
    public static int Parse(this string str)
    {
        return int.Parse(str);
    }
}

こうっぽいです。第一引数の前にthis修飾子をつける。
そしたらその型のうしろにドットで呼び出せるぜ~ということだそうで。
詳しくは別ページな!と言われたのでこれくらいにしておきます。まあ使用にあたって十分な情報は見たかな・・・?

using static

ちょっとこんがらがりそうだったので先に言葉を書きます。

  • using(名前空間~)とは別物
  • クラス(の中の静的メソッド)を呼び出したい時に使える。
    (usingがアクセスしているのは名前空間であってクラスとかではない)(個人的に混ざっていたポイント)
  • (メソッドが静的なら)静的クラスでなくとも使える。

というかんじ。

サンプル的には、

using System;
using static System.Math; // ←こーすると

class Program
{
    static void Main()
    {
        var pi = 2 * Asin(1); // ←Mathクラスのメソッドがそのまま呼べる
        Console.WriteLine(PI == pi);
    }
}

というかんじ。
もうひとつオマケ的に、列挙型の話がきました。

using static Color;

class UsingStaticEnum
{
    public void X()
    {
        // enum のメンバーも using static で参照できる
        var cyan = Blue | Green;
        var purple = Red | Blue;
        var yellow = Red | Green;
    }
}

enum Color
{
    Red = 1,
    Green = 2,
    Blue = 4,
}

enumさんも中身はSystem.Enumクラスである(?)(あってますよね?)ということで、
その中身たる静的メンバー各位には同じやり方でアクセスできますよと。

ちょっと慣れない話題がきてビビりました。

演習です。

Append Day2 演習

「ヴェッ!? な なに 構造体!?」とかなってしまったのでTriangleのほうだけ・・・

    class Triangle {
        private Point a;
        private Point b;
        private Point c;
        public Triangle(Point a, Point b, Point c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }

        private double GetDistance(Point o, Point p) { // 実装場所がメッチャ不適切ながら...
            var dx = o.x - p.x;
            var dy = o.y - p.y;
            return Math.Sqrt(dx * dx + dy * dy);
        }

        public double GetPermiter() {
            return this.GetDistance(this.a, this.b)
                + this.GetDistance(this.b, this.c)
                + this.GetDistance(this.c, this.a);
        }
    }

staticどこいった。


Append Day3

~継承~

オブジェクト指向だ!つかまえろ!

「xx is A」とかのやつですね。
これわりと整えて言葉にしようとすんのムズいなとなりました。

なんか実装になぞらえて言ったらいいかしら。

  • アクションゲームでもつくるか
  • アイテムがたくさん転がるかんじにしたい
  • アイテム:銃 拾える 投げられる 装備できる
  • アイテム:パン 拾える 投げられる 食べられる
  • どのアイテムも拾って投げるの出来るんだけど全部実装すんのダルくね?
  • Class アイテム作るか
  • それを元に(継承して)あらゆるアイテム実装できたら便利じゃね?

僕の感覚ではこうなりました。導入おわり。そいでは構文サイドの話。

クラスの継承
    class Item {
        public string Name { get; set; } = "noname";

        public void throwing() {
            // とんでく
            return;
        }
    }

    class Gun : Item {
        public int Attack { get; set; } = 0;
        // 装備できるなら攻撃力とかがあるんちゃう(ざっくり)
    }

    class Bread : Item {
        public int Recovery { get; set; } = 0;
        // たべれるんなら回復量とかがあるんちゃう(ざっくり)
    }
}

クラスの定義のときにコロン:でつないで抽象元を指定できる。
あ、このときItem相当を基底クラス(スーパークラス)、
GunBread相当を派生クラス(サブクラス)といいます。

    class Program
    {
        static void Main(string[] args) {

            var hogeGun = new Gun();

            Item box = hogeGun; // Item型にGun型
        }
    }

そしてこの操作による影響が内側(メンバーの継承)だけではない話。
基底クラス型の変数で扱える。逆はダメ。

コンストラクタまわり

ひっかかったことあるんですよね。

派生クラスのインスタンスを生成する際、
自動的に基底クラスのコンストラクタも呼び出されます。
しかし、この際、呼び出されるコンストラクタは引数なしのコンストラクタになります。
基底クラスの引数つきのコンストラクタを呼び出すためには、
以下のように自分でコードを書いて明示的に基底クラスのコンストラクタを呼び出す必要があります。

これ。
引数付きのコンストラクタしか用意してなくて、
派生側で静的解析に引っかかり続けていたおはなしとか。

    class Item {
        public string Name { get; set; } = "noname";
        
        public Item (string name) {
            this.Name = name;
        }

        public void throwing() {
            return;
        }
    }

    class Gun : Item {
        public int Attack { get; set; } = 0;
        // 装備できるなら攻撃力とかがあるんちゃう(ざっくり)
        
        public Gun(string name) : base(name) {
            // Item側で入るので引数これだけだとあんまり書くことはない
        }

    }

というわけでこんなかんじ。
base()に渡すとよしなにしてくれました。
書く順序が怪しくなりましたが、実行順は基底のコンストラクタ→派生のコンストラクタ。

アクセス修飾子protected

protectedは継承先からならアクセスできるよ、の意。
正直private, public以外をちゃんと使えていないところがあるんですが、
この機会にちゃんと意識して使っていきたいですねそうですね・・・。

基底クラスメンバの隠蔽

細かい知識が出てき始めて「やっぱufcppさんスゲェな」となっています(小声)

派生クラスで同じ名前を設定すると上書きされるやで、というおはなし。
・・・シャドウイングだな?(たぶん違う)(けど実際似てるな)

で、C#だとそれをメソッドでやると警告が出るそうな。

class Derived : Base
{
  //基底クラスのメンバーを隠蔽するには new を付ける必要がある。
  public new void Test()
  {
    Console.Write("Derived.Test()\n");
  }
}

けど、newで明示できる。良い予約語の使いまわし方だ・・・。(変な感心)

継承禁止

そんなのがあるのかって感じですけど。

sealed class SealedClass { }

sealedをつけると継承ができなくなる。はえー。

今回は演習ありませんでした。
後半結構写経になってしまいましたね・・・。


Append Day4

~抽象クラス・抽象メソッド~

継承前提のやつら。
* 抽象クラス
インスタンス化できない。継承をして使う。
GunってアイテムとBreadってアイテムはあるけどItemってアイテムは無いな?という需要。
定義時にclassの前に修飾子としてabstractをつける。

  • 抽象メソッド
    抽象クラス内でのみ定義できる。出入りの型だけ指定する感じ。
    どの継承先でもthrowingできるけど、モノによって効果違うな?という需要。 これもまた定義時に修飾子としてabstractをつける。
    abstract class Item {
        public string Name { get; set; } = "noname";
        
        public Item (string name) {
            this.Name = name;
        }

        public abstract void Throwing();
    }

    class Gun : Item {
        public int Attack { get; set; } = 0;
        // 装備できるなら攻撃力とかがあるんちゃう(ざっくり)
        
        public Gun(string name,int attack) : base(name) {
            this.Attack = attack;
        }

        public override void Throwing() {
            Console.WriteLine("正面に{1}ダメージ",Attack);
        }
    }

    class Food : Item {
        public int Recovery { get; set; } = 0;
        // たべれるんなら回復量とかがあるんちゃう(ざっくり)

        public Food(string name,int recovery) : base(name) {
            this.Recovery = recovery;
        }

        public override void Throwing() {
            Console.WriteLine("正面に{1}回復", Recovery);
        }
    }

Append Day4 演習

    abstract class Shape {
        abstract public double GetArea();
        abstract public double Perimeter();
    }

    class Triangle : Shape {
        private Point a;
        private Point b;
        private Point c;

        public Triangle(Point a, Point b, Point c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }


        public override double Perimeter() {
            return Point.GetDistance(this.a, this.b)
                + Point.GetDistance(this.b, this.c)
                + Point.GetDistance(this.c, this.a);
        }

        public override double GetArea() {
            double bx = b.x - a.x;
            double by = b.y - a.y;
            double cx = c.x - c.x;
            double cy = c.y - c.y;
            return Math.Abs(bx * cy - cx * by) * 0.5;
        }
    }

    class Circle : Shape {
        private double r;

        public Circle(double r) {
            this.r = r;
        }

        public override double Perimeter() {
            return 2 * Math.PI * r;
        }

        public override double GetArea() {
            return Math.PI * r * r;
        }

(この演習に入るに当たって出題元の多態性のページを上から下まで見ることになったなど)
(ぶっちゃけこのコースに組み込んだほうがよさそうな重要ページだったなとか)


Append Day5

~インターフェース~

ガワのやつ。IDisposableとかLINQのIEnumerableとか。メソッド名とかだけを指定しておくやつ。
「さっきやった抽象クラスと同じじゃね?」な感じに対して、アンサー。↓

クラスとよく似ていますが、インターフェースには以下に挙げるような特徴があります。 * メンバー変数を持つことが出来ない。 * static メソッドを持つことが出来ない。 * 宣言したメソッド・プロパティはすべてpublic abstractになる。 * 1つのクラスが複数のインターフェースを実装できる。

と。より抽象的な感がありました。(感想)
大きいのは、何より多重継承の代替となる部分だと思います。最後のやつ。複数実装可。

とりあえず文法。

interface インターフェース名 {
  メソッド・プロパティの宣言
}
class クラス名 : インターフェース名 {
  クラスの定義
}

ぶっちゃけこのあたりを定義から使ったことは・・・ないんですよね・・・
ちゃんと設計しないと登場しなさそう・・・ちゃんとした設計って何・・・(ひどい)

struct Id : IComparable<Id>, IEquatable<Id>{
    public int Value { get; set; } // intを持ったId型
    public int CompareTo(Id other) => Value.CompareTo(other.Value); // Id型の比較を定義(中身はint)
    public bool Equals(Id other) => Value == other.Value; // Id型の等価を定義(中身はint)
}

なので紹介されたサンプルをそのまま持ち込む弱いプレイング。
CompareTo()を持ったIComparableさん、
Equals()を持ったIEquatableさんをそれぞれ実装。
IComparable(IEquatable)型に対する処理に適用できるようになるやで~という図。

メンバーの重複、明示的実装

複数のインターフェースを実装して、
それらに同じ名前のメンバーがあったらどうすんのというおはなし。

結論的には * 共通の処理になるなら気にせずに書いて問題ない * 区別したい時は指定を入れる * 利用するときはそのインターフェース型にキャストしてから呼び出す * 直接利用できなくなる

ということだそうで。
このへん、技術的以前に論理的に矛盾が無いように 仕様を決める人ってすごいっすよね・・・(外野の感想)

サンプル。

using System.Collections.Generic;

interface IAccumulator {
    void Add(int value);
    int Sum { get; }
}

interface IGroup<T> {
    void Add(T item);
    IEnumerable<T> Items { get; }
}

継続足し算をしたいIAccumulatorくんのAdd()、要素を追加したいIGroupくんのAdd()
わけて実装するにはこう。

class ExplicitImplementation : IAccumulator, IGroup<int> {
    void IAccumulator.Add(int value) => Sum += value;
    void IGroup<int>.Add(int item) => _items.Add(item);

    public IEnumerable<int> Items => _items;
    private List<int> _items = new List<int>();

    public int Sum { get; private set; }
}

.なので、呼び出す時と同じ形で「よう考えられてんな~」となるなど。

派生した話題が多かったので元ページの全てを網羅はしていませんが、演習。

Append Day5 演習

    interface IShape {
        double GetArea();
        double Perimeter();
    }

    class Triangle : IShape {
        private Point a;
        private Point b;
        private Point c;

        public Triangle(Point a, Point b, Point c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }


        public double Perimeter() {
            return Point.GetDistance(this.a, this.b)
                + Point.GetDistance(this.b, this.c)
                + Point.GetDistance(this.c, this.a);
        }

        public double GetArea() {
            double bx = b.x - a.x;
            double by = b.y - a.y;
            double cx = c.x - c.x;
            double cy = c.y - c.y;
            return Math.Abs(bx * cy - cx * by) * 0.5;
        }
    }

    class Circle : IShape {
        private double r;

        public Circle(double r) {
            this.r = r;
        }

        public double Perimeter() {
            return 2 * Math.PI * r;
        }

        public double GetArea() {
            return Math.PI * r * r;
        }
    }

修飾子変えただけだぞ・・・?これでいいのか・・・?となりました。いいっぽい。


Append Day6

~デリゲート~ (元はコレクション操作でしたが、  対応ページがパっとしなかったので次の「例外・デリゲート」を分割しました)

「アタイ知ってる! なんか関数の型!」
とか書いて気付いたんですけど、そういやメソッドと関数って言い分けるべきものなんですよね。
怒られるな・・・あまりにも気にせずに喋りすぎていた・・・(今更すぎる)

元のページには色々順番に書いてあるんですが、
このへんの機能は「ぶっちゃけ使わないじゃん?」みたいなものがあったはずなので整理しつつ。

  • delegateという機能がある。段階としてはType宣言みたいな。
    delegate bool Hoge(int n);で、"引数にintを1つ取ってboolを返す関数"っていう型Hoge
    Hoge huga = /*なんか具体的な関数*/という感じで使う。
  • そしてこれはジェネリックFunc<int,bool>とも書けるようになるのであった
    じゃあほとんどの場合でHoge宣言いらなくね?となる(名前だけ先に付けるから読みやすさも微妙)
    (Funcがそもそも標準で用意されたdelegate T Func(S)だというんでもあるものの)
  • ちなみにvoid(戻り値なし)のときはAction<>を使います

ここまででひとつ。

  • それから匿名関数という機能も生まれました
    その時限りの関数(処理)を書ける。
    さっきのデリゲートで関数を変数として扱えるので、
    引数として関数を受け取る処理があったりして、そういうときに書いたりする。(ややこしい)
  • 文法的にはdelegate(int n){ return n > 10; } 引数nが10より大きいかを返す
  • そしてこれも後々(int n) => n > 10;と書けるようになるのであった・・・(ラムダ式

ここまででふたつ。(全部でふたつですけど)

というかんじで、諸々あわせて

// いろいろながいやつ
    delegate bool Hoge(int fuga);
    Hoge hoge = delegate (int piyo){return piyo > 10;};
// みじかくなったやつ
    Func<int, bool> hoge = piyo => piyo > 10;

と書けるようになるなどしました。
(=>の後が複数行の時はカッコでくくるとかreturnとかが必要)

とまあ色々、一段階抽象的な話題であるところにプラスして
時系列的な動きも加わったりとややこしい話題だなと以前感じたので饒舌になってしまいました。

こうぶっちゃけた話、横道でF#とかもやっている関係上、さすがに慣れた話題なのかもしれません。

とか言ってたら演習もありませんでした。 文法だけ困ったら、またラムダ式のページとかを見るとします。


Append Day7

~例外~

ぬるぽとか領域外とかInt.Parseしようとして数字がないとかそういうの。
「これ処理できんやん!プログラム落とすで!」とかそういうの。(そういうのか?)

それを野放しにするんではなくて処理として予定しておいて安全に扱いたい機能。
こんな感じにかく。↓

try {
  例外が投げられる可能性のあるコード
}
catch(例外の種類) {
  例外処理コード
}
finally {
  例外発生の有無にかかわらず実行したいコード
  リソースの破棄などを行う
}

正しいか謎ですけど、tryという単語全般に例外対処っぽい意味がありますかね。
(Parse()と、それに対するTryParse()とかのことを言っています)

throw

自分で例外を投げることもできます。

    static void fiveDakeUketukeru(int n) {
        if (n != 5) {
            throw new ArgumentException();
        }
    }

ブチギレマジキチ関数を宣言してしまいましたがそんなかんじ。
運用上は、さっきのtry{}内でこういう関数が呼ばれる感じですね。
あるいはやっぱりぬるぽとか例外とかゼロ除算とかしそうな部分。

例外の伝搬

もういっこピックアップしておいたほうがよいかなという話題。

using System;

class Program {
    // A で投げた例外が
    static void A() => throw new NotImplementedException();

    // B → C と伝搬して
    static void B() => A();
    static void C() => B();

    static void Main() {
        try {
            C();
        }
        catch(NotImplementedException) {
            // 最終的にここでキャッチされる
            Console.WriteLine(ex);
        }
    }
}

またサンプルそのまま引用なので恐縮ではあるんですけど。
考えたら当然といえば当然ですが、
処理のスタックの奥の方で起きた例外は呼び出し元にどんどん連なっていきますよという。

具体例でモリモリやらないとこれくらいかしら・・・?例外でした。


なげえ。

えっととりあえず以上です。"Day"の表記とはなんだったのか。

ぶっちゃけこんなんチマチマやってらんねえよなとか
さっさとモノつくりながらやってったほうがいいよなとか思ってしまって、
ちょっとスピード上げるかと思った結果です。

とかいいながら2.5日分くらいかけて1記事起こしてる説はあるんですけど。
それでもまあいいですよね。こんなんして記事連ねてもしゃーない。

とは言いつつもちゃんとお勉強にもなりました。
継承から抽象化からインターフェースあたりは意識して使わなかったら回避できてしまいそうなので、
何か作りながらググって習得、というルートにもハマりにくい気がしました。

そいうではという感じで、まあ一旦区切りです。
Unityに戻ってなんか作ろうかと思わんでもないですが、
コードの状態でのUnity側の持ち物がどういうつくりなのかとかをよくわかってないので
指定の範囲内以外での記述がかなりしにくい、みたいな気持ちもあって悩んでおります。

とはいえコンソールだったりWPFに行くと表現力足らんよなとか画面作りでダレるよなとか。
まあ思い立った時に思い立った事をします。それが一番モチベが保つ。

そいでは、またこんど~。