Nuro光 for マンション開通

Nuro光 for マンションが開通した。

マンション管理会社とNuro光とNuroの工事会社の間でうまく連携が取れなくて、開通工事に来てもらうまでに1ヶ月以上かかった。 でも他の人の体験談とか聞いていると半年や1年の人もいるようなので、まだかわいい方かもしれない。

開通工事

工事業者は工事予定時間から3時間ほど遅刻してきたが、工事自体は1時間弱で割とあっさり配線完了。

最初とても不安定だったが、他のルーターとIPが競合していたのでルーター外したりなどして解決。

工事後の様子

f:id:mecha_g3:20210528201354p:plain
光ローゼット

壁に空いたコンセント穴から光ケーブルを部屋に引き込んで、光ローゼットを設置。

工事の方は「取り回しやすいように長めにしておきますね」と言って、穴から2mくらい光ケーブルを出してくれた。(写真撮り忘れた)

見た目が悪すぎたのでケーブルを壁中に押し込んで、光ローゼット自体も壁にネジで固定した。

本当は光コンセントの形のほうがスマートで良かったけど、専用の工具が必要そうなのでやめといた。

回線速度

f:id:mecha_g3:20210528200141p:plain
fast.comでの計測結果
めちゃくちゃ速いし今の所切断や極端な速度低下もない。

大きなマンションだが僕が申し込みした時点で契約数0世帯だったので、今もほぼ専有状態。

これで月額3000円程度なので今の所大満足。

Nuro光forマンション 工事前にグダった記録

4/8 Nuro光forマンション申し込み
4/13 4/16に工事予約
4/15 Nuro工事担当から管理人に電話

工事担当: 4/16開通工事します
管理人: 当マンションは事前調査が必要です
4/16の工事をキャンセルし事前調査の日程調整へ

4/22 Nuroマンション開通デスクに進捗確認

進捗なし

5/2 Nuroマンション開通デスクに進捗確認

進捗なし

5/11 Nuroマンション開通デスクに進捗確認 マンション管理人にヒアリング

進捗なし
管理人: 事前調査の日程調整が進んでいない。先方から連絡が来るのを待っている。

5/13 Nuroマンション開通デスクから電話

開通デスク: マンションの側の都合で配線ができない。契約キャンセルします。
自分: それはおかしい待ってくれ。
管理人: あれからNuroと連絡していない。事前調査しないと開通できるかわからない。

5/17 Nuroマンション開通デスクに電話

開通デスク: 別部署から電話するので待ってくれ

5/20 Nuroマンションサポートから電話(多分偉い人)

サポート: マンション都合で開通できないとの事なのですが..
自分: 事前調査していないのにそれはおかしい。Nuroの設備は共用部まで来ていて自分の部屋まで配線できるかどうかの問題。業者が見ないとわからないはず。
サポート: 事前調査というサービスは行っていないので直接開通工事という体で伺いたい。管理人に話を通してもらえないか。
自分: 伝言ゲームになるの嫌なので、この電話つないだまま管理人室行きます。
管理人: 事前調査しないと開通できない事になってます。
管理人上長(?)にも連絡し会議がはじまる。
サポートと管理人直接電話。
事前調査なしで開通工事を行うことに決定 後日工事日の調整連絡

まとめ

Nuroの下請け工事業者が(事前調査など)イレギュラーな要望に対応できない様子。
一ヶ月放置された上に適当な対応されて怒。
マンション管理会社といつでも連絡できる窓口(物理)があったおかげで合意が取れた。

開通できるといいなあ。。

Effective C# 第5章

Effective C# 6.0/7.0 | Bill Wagner, 鈴木 幸敏, 鈴木 幸敏 |本 | 通販 | Amazon

個人的な読書ノートであり、自分の解釈が誤っていたり要点がずれてる可能性があります。内容が気になる人は自分で読んでください。

読了!

5章 例外処理

要求された処理が実行できない場合には常に例外をスローするべき

45. 契約違反を例外として報告すること

  • 正しく実行できない場合に例外をスローして失敗を報告する
  • 例外をフロー制御のために使用してはいけない
  • フロー制御に使われないためにも、テスト用のメソッドも提供するべき
  • DoWork, TryDoWork パターン

46. using および try...finally を使用してリソースの後処理を行う

  • IDisposableに using を使うとコンパイラtry...finally ブロックを生成する
  • using (obj as IDisposable) {...} というテク
  • リソース確保は using内または tryブロック内で行うこと

47. アプリケーション固有の例外クラスを作成する

  • 例外を発生させた問題に対して、異なる対応が必要になることが確実である場合のみ別の例外を用意するべき
  • 起こり得るすべてのエラー状態にたいして例外を用意する必要はない
  • エラーを修正できるような情報を含んだ独自の例外クラスを作成する
  • Exceptionクラスは4つ全てのコンストラクタを定義するべき
  • InnerException をつかって下位のエラーを上位に伝える

https://hackers-high.com/c-sharp/develop-self-exception/

48. 例外を強く保証すること

  • 例外安全保証レベル
    • 基本的な例外保証: リソースリークを発生させず、すべてのオブジェクトが正しい状態であり続けることを保証
    • 強い例外保証: 基本的な例外保証 + 例外のスロー後もプログラムの状態が変化しないことを保証
    • 例外をスローしない保証: 例外をスローさせないようにすることですべての操作が失敗しないことを保証
  • 強い例外保証:処理が完了するか、そうでなければ何も起こらないようにする。
  • オブジェクトのコピーコストはマネージ環境ではほとんど無視できる。
  • 例外によってプログラムが強制終了する場合強い例外保証をする意味はない。
  • Envelope/Letterパターンで、内部データ入れ替え処理を1つのオブジェクトに隠蔽できる。
  • when 句で例外をスローしてはいけない。
  • デリゲートに登録されるメソッドの中で例外をスローしてはいけない。

オブジェクトのコピーコストはマネージ環境ではほとんど無視できる

信じられない。

このためだけにEnvelope/Letterパターン使うの記述量が多すぎる全然便利に思えない。

49. catch からの再スローよりも例外フィルタを使用すること

  • catch 句に条件ロジックを追加するのではなく、例外フィルタ(when) を実装するべき。
  • catch (TimeoutException e) when (retryCount++ < 3)
  • スタック巻き戻しが起こる前に例外フィルタが評価されるのでエラー情報をより多く確保できる。

50. 例外フィルタの副作用を活用する

  • 例外フィルタで常に false を返すようにして、すべての例外のログを残すことができる。
  • デバッグ実行中はcatchしないようにすることもできる。
  • try ブロック内の変数が残った状態デバッガにアタッチできてデバッグが捗る。
public static bool ConsoleLogException(Exception e)
{
    WriteLine("エラー{0}", e);
    return false;
}

try {}
catch (Exception e) when (ConsoleLogException(e)) {} // すべての例外のログを残せるテク
catch (TimeoutException e) when ((failures++ < 10) &&
  (!System.Diagnostics.Debugger.IsAttached)){ ... } // デバッグ実行中はcatchしないテク

これはいいことを知った。早速使っていこう。

Effective C# 第4章

Effective C# 6.0/7.0 | Bill Wagner, 鈴木 幸敏, 鈴木 幸敏 |本 | 通販 | Amazon

個人的な読書ノートであり、自分の解釈が誤っていたり要点がずれてる可能性があります。内容が気になる人は自分で読んでください。

難しくなってきたのはもちろん、翻訳怪しいところとか誤植がちらほら。 内容理解に苦しむ。

4章 LINQを扱う処理

  • ありとあらゆるデータソースに対して、同じ処理を同じ言語機能で実装できるようにするのが目標。
  • 独自のデータプロバイダを提供することも可能。

29. コレクションを返すメソッドではなくイテレータを返すメソッドとすること

  • 配列全体を返すのではなく IEnumerable<T> を返そう。
  • 効率的にコレクションを保持、生成できるようになる。
  • 必要な分だけを生成できる。
  • 最初の要素の要求時まで引数チェックが遅れる問題はメソッドを分離する。

30. ループよりもクエリ構文を使用すること

  • 読みやすくなることが多い。
  • シーケンスを処理するステップごとの中間データを保持する必要がない。 - Take, TakeWhile, Skip, SkipWhile, Min, Maxなどはメソッド呼び出し形式でしか使用できない。
  • クエリ構文が使えるなら使う、使えないならメソッド呼び出し形式を使う。
  • パフォーマンスのため再実装する前に、.AsParallell() による並列実行を試す。

個人的にはループによる構築のほうが読みやすく感じてしまう。いままでメソッド呼び出し形式しか使ったことがないが、SelectManyを含む場合はクエリ構文のほうが読みやすいな。

31. シーケンス用の組み合わせ可能なAPIを作成する

  • IEnumerable<T> を引数にうけとり、 IEnumerable<T> を返すような関数を作って再利用しよう。
  • yield return を使って書くと、継続可能な、遅延実行される関数になる。
  • 複数の処理を組み合わせた場合も、シーケンスの走査は1回しか行われないのでパフォーマンスがよくなる。
  • 入力シーケンスが参照型のときは書き換えられるけど書き換えたらダメ。

よく使うであろうシーケンス変換はこのようにしたい。

パフォーマンス良くなると言っても計算オーダーは変わらない。ランダムアクセス可能であることを利用したり、コレクション全体を保持することにより計算量を落とせるアルゴリズムも多いので、いつでも使えるわけじゃないだろうし、無理してつかうものでもないだろう。

32. 反復処理を Action, Predicate, Func と分離する

  • (1) シーケンスの反復の仕方を変える Func, Predicate
  • (2) 各要素に処理を実行する: Action
  • 機能を分離しておくことで、再利用性が高まる。

33. 要求に応じてシーケンスの要素を生成する

  • 要求されたタイミングで必要な分だけ要素が生成されるので、パフォーマンスがよくなる。

事前に一気に生成しておいたほうがパフォーマンスよい場合が多いので、同意できない。

34. 関数引数を使用して役割を分離する

  • コンポーネント間に必要な制約を記述するのに最適な言語機能を選ぶ。親クラス、インターフェース、関数引数など。
  • インターフェースを使用したり、親クラスを用意する方法は、利用者になかなか手間をかけることになる。
  • ジェネリックメソッドと関数引数で代用できることがある。
  • 例外処理など実行時エラー処理にかかる手間が増えるが、柔軟性が手に入る。

35. 拡張メソッドをオーバーロードしないこと

  • 拡張メソッドは型の機能を拡張する方法としては不適切。
  • usingステートメントの差し替えで動作を切り替えるのは間違ってる。
  • 拡張メソッドは、理論的に型の1機能とみなせるような機能に対してのみ使用すべき。

36. クエリ式とメソッド呼び出しの対応を把握する

  • コンパイラがクエリ式をメソッド呼び出しへと変換する。
  • コンパイル時点でシグネチャが一致するかどうかしか見てない。
  • where → Where
  • select → Select
  • orderby → OrderBy(Descending), ThenBy(Descending)
  • group by into → GroupBy (詳細?)
  • 複数の from → SelectMany
  • join → Join, GroupJoin

37. クエリを即時評価ではなく遅延評価すること

  • 遅延評価をうまく使うことで、かんたんにクエリを合成する事ができる。
  • 即時評価を使用すべき明確な理由がないのであれば、遅延評価を採用すべき。
  • スナップショットが必要なときは ToList, ToArray を使う。
  • Where, OrderBy, Max や Min はシーケンス全体を要求する。
  • クエリの早めにフィルタ関数を通すほうが良い。

38. メソッドよりもラムダ式を使用すること

  • クエリを共通化するのにIQueryable<T>をつかおう。staticメソッドなどを使って分離してはいけない。
  • クエリ構文のままラムダ式として保持しておけば、クエリ構文として再利用できる。
  • LINQ to SQL では IQueryable<T> クエリツリーが SQL へと変換される。

39. FuncやAction内では例外をスローしないこと

  • Actionの中で例外を使わない。
  • 例外が発生しうるActionを使う場合は、新しいシーケンスを返すクエリにする。成功を確認してから置き換える。

40. 即時実行と遅延実行を区別すること

  • 計算結果を引数にわたすか、計算関数をメソッドを渡すべきか。
  • 最大の判断基準は副作用。
  • 副作用を起こさず、即時評価も遅延評価も同じ結果になるなら、パフォーマンスで判断できる。

41. コストのかかるリソースを保持し続けないこと

  • クロージャやキャプチャされた変数によってObjectの生存期間が延長される。
  • ファイル読み込みをIEnumerable経由で行う → いつファイルを閉じれるか問題が発生
    • ファイルを開いてIEnumerableを返却するメソッドにする → 2回走査されると死
    • アルゴリズムを引数として渡すパターン → ちょっと複雑
  • ToList などにより即時評価させることで、すぐリソースを破棄できるようにするテクもある
  • C#コンパイラは1スコープにつき1ネストクラスを作成してクロージャを実装する。不本意にコピーされないか注意する。

42. IEnumerableとIQueryableを区別すること

  • IQueryableはIEnumerableを継承している
  • IQueryableは式ツリーとして処理され、データソースに近いところ(SQL分などとして)実行される
  • IEnumerableローカルマシン上で実行される。
  • データソースが IQueryable に対応しているなら積極的に使うべき。
  • AsQueryable() で IEnumerableもIQueryableに変換できる。

43. クエリに期待する意味をSingle()やFirst()を使用して表現すること

  • ちょうど1要素取るなら Single()
  • 0要素だったり2要素以上ある場合は例外になる
  • First(), FirstOrDefault(), Skip(2).Fist() などをつかう。
  • コードの意味を明確化できる

44. 束縛した変数を変更しないこと

  • C#はクエリおよびラムダ式を以下ののいずれかに変換する
    • 変数参照なし → staticデリゲート
    • メンバ変数の参照 → インスタンスデリゲート
    • ローカル変数やメソッドの引数 → クロージャ 
  • クロージャにキャプチャされた束縛変数は変更しないようにするべき

Effective C# 第3章

www.amazon.co.jp

個人的な読書ノートであり、自分の解釈が誤っていたり要点がずれてる可能性があります。内容が気になる人は自分で読んでください。

だんだん内容が濃くなってきて、サクサクは読み進めれなくなった。でもその分役立つポイント多くて面白い。

3章 ジェネリックによる処理

18. 最低限必須となる制約をつねに定義すること

  • 型引数への制約を行わない場合、型引数はSystem.Objectに定義されたインターフェースしか扱えない。必要な制約は実行時にチェックする必要あり
  • 型引数への制約を追加すると、System.Objectを超えたインターフェースを型引数に期待できる。必要な制約はコンパイル時にチェックされる
  • あらゆる型引数に対応するクラスを作るのが大変。制約をつけることでクラス作成者の負担が減るが、かわりに利用者は利用しにくくなる。
  • 型引数に制約を追加することで得られる安全性と、制約に従うために必要となる作業量のバランスを取る。
  • 前提条件を最小限に抑えつつ、本当に必要となる条件だけをすべて制約として指定する。

最低限必要な制約が何か、自信を持つためには言語仕様やあらゆる標準インターフェースの継承関係など理解してないといけなくて超ハードじゃないですか?

19. 実行時の型チェックを使用してジェネリックアルゴリズムを特化する

public sealed class ReverseEnumerable<T> : IEnumerable<T> { ... }

と定義して実装することで、色々な列に対応できる。 ランダムアクセス性があるIListに特化した実装を用意したいときは以下のようにする。

// 定義
IEnumerable<T> sourceSequence;
IList<T> originalSequence;

// コンストラクタ内
collection = srcCollection;
originalSequence = collection as IList<T>;

// IListにキャストできたかどうかで実装をかえる。
if (originalSequence == null)
{
    ...
}
  • 特定の型に特化した実装をジェネリッククラスの内部に隠蔽することが目標。
  • 再利用性を最大限に高めつつ、特定型に特化した実装を提供することができる。

20. IComparableとIComparerにより順序関係を実装する

  • IComparable:自分自身と他のオブジェクトを比較
  • IComparer:引数で与えられた2要素の比較
  • ジェネリック版のIComparableもあわせて実装すべき。
    • 明示的に int IComparable.CompareTo(object obj) と宣言する。
  • 演算子オーバーロードも実装するとよい。
  • static Comparsion<T> デリゲートを公開して他のジェネリックAPIでも使えるようにする。
  • 他の要素で比較するクラスを内部クラスとして定義してやるとよい。
  • 同値性==は必ずしも実装しなくてよい。

21. 破棄可能な型引数をサポートするようにジェネリック型を作成すること

  • IDisposableを実装した型引数を考慮する。
  • ローカル変数の場合:IDisposable実装していなくてもusing使って問題ない。
  • メンバ変数にする場合:IDisposableを実装する
  • sealed classにすることでIDisposableの実装を簡略化できる。

22. ジェネリックの共変性と反変性をサポートする

  • C#4.0以降の機能
  • 共変 = covariant = out
  • 反変 = contravariant = in
  • 不変 = invariant
  • 上記まとめて分散性というらしい
  • インターフェースやデリゲートを作成する場合、できる限りin, out キーワードを指定する。
  • 難しく考えず入力専用の型ならin, 出力専用の型ならout を指定すれば良い。

最後の段落のまとめに誤植(共変反変が逆になってる)があって悩んだ.. これは使ったことない機能で勉強になった。

23. 型パラメータにおけるメソッドの制約をデリゲートとして定義する

  • たいていの場合にはクラスやインターフェース制約を指定するのが適切。
  • このクラス使うにはこのインターフェースを継承して使ってね、という感じでクラスを作るとき、インターフェースはデリゲートで代替したほうがいい。
  • ほとんどの場合、ジェネリッククラスで呼び出す必要のあるメソッドは特定のデリゲートとして置き換え可能。

これは役にたった。

24. 親クラスやインターフェース用に特化したジェネリックメソッドを作成しないこと

  • ジェネリックメソッドは型引数に該当するあらゆる型と一致する。
  • ジェネリックメソッドになっているメソッドがあるときに、オーバーロードで特定のインターフェース用に特化したメソッドを作っても、ジェネリックメソッド側が使われてしまう。
  • 明示的にインターフェースにキャスト呼び出せば使える。
  • ジェネリックメソッドの中で実行時にインターフェースチェックをしても良いが、実行時オーバーヘッドがかかるので大きなクラスではやらないほうが良い。

なるほど。知らないとやってしまいそうだった。

25. 型引数がインスタンスのフィールドではない場合にはジェネリックメソッドとして定義すること

  • ジェネリッククラスの型引数に制約を与えると、クラス全体に制約がかかる。
  • ジェネリッククラスの中に、ジェネリックメソッドを定義することができる。
  • さらに非ジェネリックメソッドを定義することで、その型に特化した実装を提供できる。
  • 呼び出す側は型引数なしで呼べるので、ジェネリック版よりも最適化された実装があれば、自動的にそちらが使われるようになる。
  • ジェネリッククラスにするかジェネリックメソッドにするか
    • 型パラメータの値をクラス内部状態として保持するかどうか
    • ジェネリックインターフェースを実装する型かどうか

26. ジェネリックインターフェースとともに古いインターフェースを実装すること

  • クラス及びインターフェース, publicプロパティ, シリアル化の対象とした要素 が該当する。
  • IComparable<T> を実装するなら IComparable も実装せよという話。
  • 古いAPIとの互換性や、異なる型の間で順序や同値関係を構築したい場合には非ジェネリック版を扱う必要がある。

正直同意できない。 書いたところでろくに動作確認してないコードになってしまいそう。 本当に必要になってから実装すればええんじゃ。。

27. 最小限に制限されたインターフェースを拡張メソッドにより機能拡張する

  • 必要最低限のメソッドだけをインターフェースに定義し、拡張メソッドで補助的なメソッドを増やす。
  • インターフェースのデフォルト機能をメソッドとして提供できるようになる。

驚き。拡張メソッドはメソッドを外から追加しているようで気持ち悪いので使いたくない派だった。 確かに最近のライブラリはこの実装方法をよく見かける。勉強になった。

マーカーの例は誤植か、最後は new MyType2(); では?

28. 構築された型に対する拡張メソッドを検討すること

  • 具体的な型引数を指定したジェネリッククラスに対して、拡張メソッドを提供する。
  • アプリケーションの中に出てくるストレージモデル(構築された型で作られる)やインターフェースに対して拡張メソッドして機能を実装するのが最善策。
  • ジェネリック型のインスタンスを生成するだけで必要な機能がすべて揃った状態にできる。ストレージとモデルも分離できる。

なるほど。拡張メソッド使っていこう。

Effective C# 第2章

www.amazon.co.jp 個人的な読書ノートであり、自分の解釈が誤っていたり要点がずれてる可能性があります。内容が気になる人は自分で読んでください。

第2章 リソース管理

GCやメモリ管理や初期化の話。

11. .NETのリソース管理を理解する

  • GCは参照関係を把握していて、どこからも参照されなくなったオブジェクトを削除し、メモリコンパクション(再配置)を行う。
  • アンマネージドなリソースを開放するにはファイナライザとIDisposableを使う。
  • ファイナライザは特定のタイミングで実行されるわけではない点に注意する。
  • GCはファイナライザを呼び出す準備が整ったオブジェクトをキューしておき、次回GCでファイナライザ実行、その次回にオブジェクトを削除する。
  • ファイナライザを持つクラスは、GCの世代(generation)の実装の影響もあるので、予想以上に寿命が伸びることがある。
  • IDisposebleの実装を利用することでこのパフォーマンス低下を避けることができる。

12. メンバには割り当て演算子よりもオブジェクト初期化子を使用すること

  • すべてのコンストラクタに共通した初期化を行う場合は初期化子するのがよい。
  • コンストラクタがいくつ追加されても漏れなく初期化されるから安全。
  • コンストラクタを書かなかった場合にはデフォルトコンストラクタが自動生成され初期化コードが埋め込まれる。
  • 初期化子の初期化は、 親クラスのコンストラクタより先 でかつ 記述された順序 で呼び出される。
  • オブジェクト初期化子を使用すべきでないケース
    • 0 や null で初期化する場合。書かなくていい。むしろパフォーマンスおちる可能性あり。
    • 複数回初期化される可能性がある場合。コンストラクタで初期化する場合は初期化子で作成したオブジェクトはゴミになる。
    • 例外処理に対応させる場合。初期化子は try ~ catch できないので、復旧するコードをクラス内に書けない。

13. static メンバを適切に初期化すること

  • staticコンストラクタはstaticメンバを初期化する最適な方法である。
  • privateメソッドなどは他の方法はよくない。
  • 初期化が重い場合は、Lazyなど遅延評価を使うことを検討する。
  • staticコンストラクタは例外が出るとプログラム停止することに注意する。

14. 初期化ロジックの重複を最小化する

  • コンストラクタをコピペするな。privateメソッドで共通化するな。コンストラクタ初期化子を使え。
public MyClass(int initialCount) : this (initialCount, string.Empty) {}
  • デフォルト引数を使うことでコンストラクタの重複を最小化できる(C#4.0以降)。
  • コンストラクタのオーバーロードよりデフォルト引数を選択するべき。デフォルト値は例外にならないようにするべき。
  • new制約を満たすため、引数なしのコンストラクタを明示的に作成しておくこと。
  • デフォルト引数の名前とデフォルト値は publicインターフェース であるので、利用するコードと強い結合を生み出す点に注意。

インスタンス初回生成時の処理一覧

  1. static 変数メモリストレージの0初期化
  2. static 変数の初期化子実行
  3. 親クラスの static コンストラクタ実行
  4. static コンストラクタ実行
  5. インスタンス変数メモリ・ストレージの0初期化
  6. インスタンス変数の初期化子実行
  7. 親クラスのコンストラクタ実行
  8. インスタンスコンストラクタ実行

2回目以降は 5からスタートする。

15. 不必要なオブジェクトの生成を避けること

  • ローカル変数として参照型をたくさんnewするとアロケーションコストが発生してパフォーマンス低下する。
  • 気になるケースではメンバ変数に昇格するとよい。
  • メンバ変数がIDisposableインターフェースを実装している場合、クラス自身もIDisposableを実装すべき。
  • メンバ変数より大域で共有したい場合はstatic変数を利用できる。DIによって外から提供するのも良いだろう。
  • stringの連結は遅いのでStringBuilderを使おう。不変型を生成するときはビルダークラスを提供しよう。

16. コンストラクタ内では仮想メソッドを呼ばないこと

  • 仮想(virtual)メソッドは実行時の型で呼ばれるメソッドが決定される。
  • 小クラスの初期化子実行、親クラスのコンストラクタ実行、小クラスのコンストラクタ実行 の順で初期化される。
  • インスタンスの初期化が不十分な状態で小クラスのメソッドが呼ばれることになる。

17. 標準的なDisposeパターンを実装する

  • 標準的なDisposeパターンを実装することが、非マネージドリソースを解放する正しい方法。正しく理解せよ。
  • Disposeの呼び出しを忘れた場合に備えてファイナライザを準備しておく。
  • 親クラスでは以下の規則に従うべき

    • リソースを解放するために、IDisposableインターフェイスを実装すること。
    • クラスが非マネージリソースを直接扱う場合に限り、防御策としてファイナライザを追加すること。
    • Disposeとファイナライザはいずれも、派生クラスにおいてリソース管理を独自にオーバーライドできるよう、仮想メソッドに処理を委ねるようにすること。
  • 派生クラスでは以下が必要

    • 派生クラスでは、独自のリソースを解放する必要がある場合に限って仮想メソッドをオーバーライドすること。
    • クラスのメンバが非マネージリソースである場合に限り、ファイナライザを実装すること。
    • 親クラスの仮想メソッドを必ず呼ぶこと。
  • 実装例 docs.microsoft.com

Effective C# 第1章

買ったので読む。 www.amazon.co.jp

個人的な読書ノートであり、自分の解釈が誤っていたり要点がずれてる可能性があります。内容が気になる人は自分で読んでください。

C#言語イディオム

1. ローカル変数の型をなるべく暗黙的に指定すること

  • 基本的に var を使う。数値型の場合は型を明記することを推奨する。
  • varはコンパイル時にコンパイラが適切な型を選んでくれる。
  • 三者がコードを読んだ際にローカル変数の型がすぐにわからない場合は明記するべき。

2. constよりもreadonly

  • constのほうがわずかにオーバーヘッドは少ないが、readonlyのほうが柔軟性が高い。
  • constは定数埋め込み。readonlyは動的に参照。
  • public定数の更新はインターフェースの更新である(バイナリ互換性を壊す)。使用箇所を再コンパイルしないと反映されない。
  • constじゃないと駄目なところ以外は使うべきではない。

3. キャストではなく is または as を使用する

  • 大前提として、型の変換は避けるべき。 どうしても変換しないといけないときは、is,asを用いて意図を明確にする。
  • キャスト演算は思わぬ動作をすることがある。
  • 一部コードでは値型と参照型に両対応するためキャストを使用している。(このため例外が出ることがある)
  • System.Obect 型はから任意の型へは変換演算子が定義されていないことを利用する。

4. string.Format を補完文字列に置き換える

  • 可読性高く文字列を生成できる。
  • 書式指定文字列(":F2"など)や、補完式(":{}")も使いなれる。
  • もちろんSQL文を作ったりするのには使わないこと。

5. カルチャ固有文字列よりFormattableStringを使う

  • FormattableString s = $"foo"; はFormattableString型になる。string.Formatに渡すformatとargumentを保持した変数になる。
  • あとはカルチャを指定してstring.Formatを実行する。
  • ローカライゼーションで非常に役立つ機能。

6. 文字列指定APIを使用しないこと

  • 型の安全性が失われる。
  • nameof()式を使うことでシンボル名を取得できるので活用しよう。

7. デリゲートを使用してコールバックを表現する

  • デリゲートはタイプセーフなコールバックを定義できるもの。
  • クラス間でデータをやり取りするが、インターフェースを使用するほど密に連携させたくない場合に最適な選択肢。積極的に使って良い。

  • マルチキャストデリゲートの注意点

    • 例外に対して安全ではない。途中で例外が出ると後ろは実行されない。
    • 返り値は最後に実行されたメソッドの返り値になる。

8. イベント呼び出し時にnull条件演算子を使用すること

Updated?.Invoke(this, counter)

簡素にかけるだけではなく、スレッドセーフなnullチェックになる。

9. ボックス化およびボックス化解除を最小限に抑える

  • 値型をインターフェース経由で操作する場合にもボックス化、ボックス化解除が行われる。
  • 値型をSystem.Objectの代わりに使用すると暗黙的に変換されやすいので避ける。(翻訳怪しい?)
struct Person {...}
var attendees = new List<Person>();
  • Listを操作するたびにコピーが発生してバグを埋め込みやすい。
  • JITコンパイラはPersonがボクシングされないようにListに特化したジェネリック型を生成する。

10. 親クラスの変更に応じる場合のみ new 演算子を使用すること

  • 親クラスの非virtualメンバを、小クラスでnewをつけると再定義できる。
  • これは静的に別のメソッドになっている。virtualメソッドは動的に結び付けられる。
  • 親クラスは必要最低限(小クラスで再定義してほしいメソッドのみ)にvirtualをつけること。
  • 小クラスは親クラスが名前を被せてきたときにのみnewを使ってもいいかもしれない。