概要
前回(『プログラミング C#』 その2 基本的コーディング)のつづき。ささっと進めるぞー。
流し読みノート
参照型と値型ってなに
- 参照型は
class
キーワードで定義された型。- この型の変数はインスタンスではなくてインスタンスへの参照を保持している。
- この型の変数を代入したとき、それはインスタンスのコピーではなく参照のコピーなのだ。
- この型の変数を
==
で比較したとき、値の内容で比較せずオブジェクトの同一性で比較する。内部的にはObject.ReferenceEquals(Object, Object)
を使って、すべての型が提供していておおよそ一意であるGetHashCode
の値を比較してるそうだ。 - すべての参照型変数は、参照がないことを意味する
null
を持てる。
わかるわかる、ここで言いたいことはよくわかるぞ。 Python のミュータブルオブジェクトのことでしょ。 Python には変更できるオブジェクト(ミュータブル)とできないオブジェクト(イミュータブル)があって、変更できるオブジェクトのほうは値を変更すると参照でつながっている違う変数の中身も変わっちゃうんだよね。リストが顕著。
# 1 だけ入れたリスト。
aaaa = [1, ]
# これはリストのコピーではなくリストの参照のコピー。
bbbb = aaaa
# もとのリストに 2 を追加。
aaaa.append(2)
# もとのリストに追加したはずなのに、ふたつめのほうにも 2 が入ってる。
print(bbbb)
# [1, 2] って出る。
C# のノートなのに Python 書いてるけど、こうちょろっと書けるのが Python のいいとこなのよね! C# ってちょろっと書けないんだもん。イチイチ VS 開いてプロジェクト作ってなんやかんや面倒くさい。そういうのレガシー(時代遅れ)っていうんだろう?
レガシーについて、僕の考えが纏まりました。
— まろ@関数型言語作曲機械学習勉強してない (@_marony) January 31, 2020
標準的な定義としてはこれでよろしいのではないかと思います。
曖昧な定義にならないように細心の注意を払っています。
後で広辞苑に載せておいてください。
レガシー
「書いてて色々めんどーくさい」
モダン
「書いててたのしい!!」
だから C# のノートで C# ディスるのやめとけって。
- いっぽう値型を作る方法……はよくわからんのだけど、値型風の挙動をするクラスは
struct
キーワードで作る。struct
で作ったものを構造体っていうんだけど、基本的にはクラスと同じらしい。- とくに違うのが、引数なしのコンストラクタを自分で書くとエラーになるそうだ。コンパイラが自動で補充するんだって。
int
が値型らしいぞ。だからnull
を持てなくて、デフォルト値は 0 だ。- ただし、
int
ではなくNullable<int>
とかint?
で宣言するとnull
を持てるint
ができるようだ。
メソッドに参照渡し
- C# のメソッド引数に
out
をつけると参照渡しになる。Foo(out int arg)
みたいな感じね。これは渡すときもFoo(out 1)
みたいにつける必要がある。明示的でいいよね。 out
ではなくref
キーワードを使うと情報の流れを両方向にできる。えーーーと? これはアレか? インスタンスとかに引数を渡したとき、インスタンス内部で変更してもインスタンス外部で変更しても値が共通になるということか。使い所、むつかしそー!- なんか、本ではこのことを「複数の値を返せるメソッド」と言っている。まあ確かにそういう言い方もできるか。 Python はふつーに複数値を返せるけどな。
だからディスるのやめなさい。
クラスの話
- 言及はとくにないけれど、 Python でいうところのインスタンス変数、インスタンスメソッドのことを総合してメンバと呼んでるっぽい。
- ほんでクラス変数、クラスメソッドを静的メンバと呼んでいる。
- 「フィールド」は「名前付きの記憶領域」だそうだ。なんじゃそりゃ。たぶんこれはインスタンス変数クラス変数のこと。
readonly
をつけるとリードオンリーになる。しかしreadonly
を変更する方法があるにはあるから、保証性はない。- マジで変えたくないなら
const
をつける。ただnull
、数値、bool
、string
、列挙型しか使えない。定数はこれで宣言すればいいね。
- 「プロパティ」はメソッドの変種で、変数の set, get 時の動きを管理する。
private int _x;
public int X
{
get
{
return _x;
}
set
{
_x = value;
}
}
// ↑これの略として
// ↓こう書く。
public int X { get; set; }
- あと「インデクサ」とかいう謎のなにかがあり、 VB という言語から引き継いで .NET に受け継がれているけれど C# ではほとんど無視されているそうだ。マニアックなやつなんやな、ロマンを感じる。
てか用語わかりづらいな。こんな感じか。
メンバ | フィールド | プロパティ | |
---|---|---|---|
インスタンス変数 | ○ | ○ | △ |
インスタンスメソッド | ○ | △ | |
クラス変数 | ○ | ○ | |
クラスメソッド | ○ |
- メンバのアクセシビリティは
private
がデフォルトだからprivate
はいちいち書かなくても OK。 - コンストラクタを書かないときは、 C# が勝手に空っぽのコンストラクタを追加してくれてる。だからある意味コンストラクタのないクラスというものは存在しないことになるんかな。
:this()
をコンストラクタにつけるとコンストラクタを実行できる。まあこれはつまり、コンストラクタのオーバーロードをたくさん実装するときに行数を減らすためのやつだろ。
クラスに演算子やキャストを実装
演算子の実装とか、他の型との演算の実装とか、型変換(キャスト)の実装とか。
// + 演算子
public static Counter operator +(Counter x, Counter y)
{
return new Counter(x.Count + y.Count);
}
// 違う型との演算
public static Counter operator +(Counter x, int y)
{
return new Counter(x.Count + y);
}
// キャスト (int)counter みたいにキャストできるようになる。
// explicit ではなく implicit を使うとキャストなしで暗黙的にキャストできるようになる。
public static explicit operator int(Counter value)
{
return new value.Count;
}
面白いから書いとくけれどこんなのゼッテー使う機会ねー。どういうとき必要になるんだろ……。
他の型
- ネスト型……これはクラスの中にクラスがあるだけだろう。 Python で関数内関数とか慣れてるからわかる。
- enum は
const
フィールドを定義するための別の方法。switch
文によく使われる。 Python では3.4から追加されたけれどこれ好きだなー。可読性があがるもんね。 [System.Flags]
で enum を定義すると|
記号で or を表現できる? みたい。var
とともに実装された匿名型はnew { X = "xxx" }
こういうやつ。それ自体に型いらないし、中身にも型がいらない。そして全プロパティが readonly。
今更だけど C# で「型宣言」って言われているときはクラス定義のことだと思ってよさげね。
その他つまみ読み
- C# の変数の命名規則は Microsoft が定義していて、 FxCop というツールでチェックできるんだって。でもこれは VS に組み込まれているから別に使うことはなさそう。
- C# にはデフォルト引数値がある。 Python にはデフォルト引数に加えキーワード引数があるぞ。キーワード引数のおかげでオーバーロードがよりスマートに実装できるハズ。
小休憩
メンバとかフィールドとかプロパティとか、用語の整理がつくと読みやすくなっていいね。参照型とか値型のことも、 Python の知識と照らし合わせてスッと入ってきてよかった。
でもなんだろ……なんか複雑に感じる。 Python のほうが、本の語り口もプログラミングを楽しんでいる印象を受けるし、そのシンプルさと美しさを誇りに思っているように見えた。 C# の本って、ただの教科書って感じがするんだよなあ。面白くない。まあ、まだ3章やしな。(あと18章)