プログラミングC#読解メモ 第三章 型 後篇

「プログラミングC#」がライトノベルくらいのボリュームならコミケ行きの18切符の旅に持っていって勉強してもよかったんだけど(今回は北回りルートを選択したので相当時間が取れる)、さすがにこの辞書を持っていくのはあまりにつらい。たぶん年内最後の更新になるのできりがいいところまでやってしまおう。

メソッド

昔ハマったこと

以下のコード。

    internal class Program
    {
        private static void Main(string[] args)
        {
            var aStruct = new TestStruct(1);

            Console.WriteLine(aStruct.a);
            MemberChange(4,aStruct);
            Console.WriteLine(aStruct.a);
        }

        private static void MemberChange(int num, TestStruct st)
        {
            st.a = num;
        }
    }

    public struct TestStruct
    {
        public int a;

        public TestStruct(int x)
        {
            this.a = x;
        }
    }

コレの出力はどうなるか。

1
1

になる。つまりMemberChange()は働かない。構造体が値渡しであることをご存知のひとならそりゃそうだろ、とすぐ理解するだろう。MemberChange()にaStructは値で、つまり双子の片割れを渡されるに等しい。双子の片割れのメンバをいくらいじったところで本家に影響がないのは当然である。
これを解決するのがref,outキーワードである。

        private static void MemberChange(int num, ref TestStruct st)
        {
            st.a = num;
        }

ref,outキーワードは引数を参照渡しする。呼び出すときにも引数にref,outキーワードをつけるのを忘れないように。

            MemberChange(4,ref aStruct);

ref,outの違いは干渉の義務の有無である。両方共処理は変わらないが、outで渡した時はそれを書き換えてないと怒られる。
参照型についてもref,outを使う意味はある。このエントリを参照。
http://ufcpp.net/study/csharp/sp_ref.html
簡潔にまとめると、参照型を値渡ししたときは、参照先の情報を変更できる。
参照渡しした時には参照先そのものを変更することができる、ということだ。
このへんが感覚的に理解できるようになりたい。

省略可能引数、名前付き引数。はい。

拡張メソッド。既存のクラスに対して勝手にメソッドを拡張できる。

        public static string Again(this string str)
        {
            return str + str;
        }

        static void Main(string[] args)
        {
            string a = "lastcat";
            string b = "yurumin";

            a = a.Again();
            Console.WriteLine(a);
           
        }

パラメータにthis修飾子をつけるとその型にメソッドを追加できる。なのでドット記法で呼び出せる。
同namespace内、またはusingディレクティブ内でのみ使用可。
ジェネリックな静的クラスのみで定義可能らしいが、まだジェネリックの意味はわかってないからなんとも。静的クラスである理由はそりゃあそうだなと思うけど。

プロパティ

基本的にはgetter.setterのアレ。
特殊なプロパティとしてインデクサというものが存在する。
ずっと前にEffectiveC#を読んだ時にもあった気がする。要は、対象オブジェクトを配列にように考え、インデックスであるような引数を取るプロパティである。

    class TestClass
    {
        int firstMember;
        int secondMember;
        int thirdMember;

        public TestClass(int num1, int num2, int num3)
        {
            this.firstMember = num1;
            this.secondMember = num2;
            this.thirdMember = num3;
        }

        public int this[int index]
        {
            get
            {
                switch (index)
                {
                    case 1:
                        return this.firstMember;
                    case 2:
                        return this.secondMember;
                    case 3:
                        return this.thirdMember;
                    default:
                        return 0;
                }
            }
        }
    }

        static void Main(string[] args)
        {
            var hoge = new TestClass(3, 54, 2);
            for(int i = 1; i < 5; i++)
            {
                Console.WriteLine(hoge[i]);
            }
        }

出力は3,54,2,0。

演算子

以下のように定義。

        public static TestClass operator +(TestClass a, TestClass b)
        {
            return new TestClass(a.firstMember + b.firstMember, a.secondMember + b.secondMember, a.thirdMember + b.thirdMember);
        }

自分が定義する演算子の引数は、一つが自身の型であれば、後は別にどんな型でも構わない。

キャスト演算子もだいたい同じように書けるが、一つオプションが有る。explicit/implicitだ。暗黙的型変換を許容するかのオプションで、implicitは許可。
true/false演算子少し取り上げられていた。これらを定義することでインタンスをそのままif()の中に入れて使うことができる。
Cとは違って0以外はfalseとかできないし、Lispと違って空リストをNILみたいなことにもなってないが、コレを使えばできる。

読めばわかることは書いてると終わらないから読んでもわからないことだけをメモしよう……