Effective C# 6.0 / 7.0 第1章 メモ
翔泳社50%OFF・50%ポイント還元をやっていたので,
思い切ってEffective C#とMore Effective C#とDDDの本を購入しました.
今はやってないですが,翔泳社5割引セールは1か月・2か月単位で実はやっています.
自分は本に色々書き込みながら覚えるタイプなので,電子書籍はKindle版よりPDF版の方が好きなんですよね.
ボクはPDF版電子書籍を購入する際,下記の記事に記載されているサイトを利用してます.
5月中までにEffective C#を,6月中までにMore Effective C#を理解して,C#力を上げたい所存.
- ローカル変数の型をなるべく暗黙的に指定する
- constよりreadonlyを使用する
- キャストにはisまたはasを使用する
- string.Format()を補間文字列に置換する
- カルチャ固有の文字列よりもFormattableStringを使用する
- 文字列指定のAPIを使用しない
- デリゲートを使用してコールバックを表現する
- イベントの呼び出し時にnull条件演算子を使用する
- ボクシング・アンボクシングを最小限に抑える
- 親クラスの変更に応じる場合のみnew修飾子を使用する
ローカル変数の型をなるべく暗黙的に指定する
local変数の型を宣言する場合はvar
で書くのを推奨.
メリット
- local変数の型の細部を気にせず,開発者にとって重要なポイントに注力できる
- かえって変数名をわかりやすくしようと考えるようになり,結果的に可読性が上がる
数値型(int,float,double)については,切り捨て等が生じないよう型を明記した方がいい.
constよりreadonlyを使用する
const
:コンパイル時定数
public const int Millennium = 2000;
readonly
:実行時定数
public static readonly int ThisYear = 2020;
実行速度:コンパイル時定数 > 実行時定数 柔軟性 :コンパイル時定数 < 実行時定数
実行時定数をなるべく使用した方がいい. コンパイル時定数はパフォーマンスが要求される場面,かつ定数値が将来のリリースにわたって変更されない場合に使う
constのバージョニング問題に注意. ⇒ 定数を定義しているライブラリ側だけでなく参照しているアプリ側もリビルドする必要がある
省略可能な引数のデフォルト値の挙動はコンパイル時定数と同じであることに注意.
// int z = 0 が省略可能な引数 private void Foo(int x, int y, int z = 0){ }
対処法としてオーバーロードを複数定義するといい.
下記の記事にて,const,readonly,static readonlyが詳しく書かれている. qiita.com
readonlyは読み取り専用であることを表明するのに使用すると良いです。 constはconst対象がprivateである場合、又は高いパフォーマンスが求められていて、 なおかつ将来にわたって変更されることがないことが明らかな場合にのみ使用するべきでしょう。 そうでなく、将来変更される可能性のある値を定数として公開する場合にはstatic readonlyの使用をお勧めします。
キャストにはisまたはasを使用する
キャストにはas演算子を利用したほうが安全かつ実行時の効率も優れる.
Unityでは例えば,InstantiateでGameObjectにキャストする時,
GameObject go = (GameObject) Instantiage(prefab);
とするのではなく,
GameObject go = Instantiate(prefab) as GameObject;
と記述する.
また,キャストが出来ない場合,
asはnull
を返すが,()キャストだとInvalidCastException
例外処理が生じる.
asは参照型への変換のみ行う.(値型がnullになることはないから)
この場合,null許容型へ変換するようにas演算子を使用し,返り値がnullかどうかチェックすればOK.
(as演算子の左辺が値型あるいは任意のnull許容型であれば有効)
object o = Factory.GetValue (); var i = o as int?; if (i != null) Console.WriteLine (i.Value);
string.Format()を補間文字列に置換する
C# 6.0 から補完文字列(string interpolation)が導入されている.
double a = 3; double b = 4; Console.WriteLine($"Area of the right triangle with legs of {a} and {b} is {0.5 * a * b}"); Console.WriteLine($"Length of the hypotenuse of the right triangle with legs of {a} and {b} is {CalculateHypotenuse(a, b)}"); double CalculateHypotenuse(double leg1, double leg2) => Math.Sqrt(leg1 * leg1 + leg2 * leg2); // 予想結果 // Area of the right triangle with legs of 3 and 4 is 6 // Length of the hypotenuse of the right triangle with legs of 3 and 4 is 5
置換メリット
Console.WriteLine(${"ユーザ名は{c?.Name ?? "名前が見つかりません"}");
String.Format()の弱点
- 生成される文字列が評価,検証されるまでその内容がわからないためミスが起きやすい
- 引数の番号表記とparams配列に指定した引数の順序が正しく一致しているか一見わからない
文字列補間を使用する場合もToString()
を付けた方がGC Allocが少なくなる.
ちなみに「ZString」を利用することで,string.FormatのGC Allocを削減することができる.
カルチャ固有の文字列よりもFormattableStringを使用する
ローカライゼーション対応が必要な場合,文字列補間機能の詳細を把握する必要がある.
特定のカルチャが必要な場合,文字列補間を明示的にFormattableString
として作成してから,
特定のカルチャを使用して文字列に変換する.
日米では少数点は.
が使われているけれど,欧州では,
が使われているらしい.
国によってドットとカンマの使い方が逆だったりする.
public static string ToGerman (FormattableString src) { return StringBuilder.Format (null, System.Globalization.CultureInfo.CreateSpecificCulture ("de-de"), src.Format, src.GetArguments ()); }
まだ使ったことの場面がないので,使うときになったら思い出す…. docs.microsoft.com
文字列指定のAPIを使用しない
文字列ベースのAPIやライブラリの問題点
- type safe ではなくなる
- ツールによるサポートがなくなる
- 静的型付け言語における利点を多く失う
⇒ C# 6.0 導入の nameof()
式を利用
クラス名・変数名・プロパティ名などを安全に文字列化でき,名前にまつわるバグを減らしてくれる
クラス名や変数名などを文字列リテラルとしてコーディングしなければならない場面では、できるだけnameof演算子を使おう。つまらないバグに悩まされずに済む。
上の記事にも書かれているように,引数に関数名を文字列で渡す必要がある時に 誤入力や後から関数名を変更するときに対して安全になる.
InvokeRepeating (nameof (NextWave), waveSpan, waveSpan);
これから文字列で渡す必要がある場合とかはnameof
を使ってみる.
デリゲートを使用してコールバックを表現する
delegateは実行時callbackを実装する場合に最適な機能で
特定のクラス間でデータをやり取りする必要があるものの,
互いのinterfaceを使用するほどには密に連携させたくない場合に最適.
client側で必要となる条件も比較的単純なもので済む.
イベントの呼び出し時にnull条件演算子を使用する
null条件演算子は.?
のこと.
イベントにハンドラが全く登録されていなかった場合,アタッチされたイベントハンドラのコードと そこから呼び出されるコードとの間で競合状態が起こる場合がある.
C# 6.0から導入されたnull条件演算子を用いることで,
これらに対応したコードをスレッドセーフに簡潔に記述でき,
安全にイベントハンドラを呼び出すことが出来る.
class MyClass { event Action<int,int> OnClick; void Foo() { OnClick?.Invoke(11, 22); } }
ボクシング・アンボクシングを最小限に抑える
ボックス化(boxing):値型を不定な参照型オブジェクトのメンバにすること. ボックス化解除(unboxing):ボックス化された値型のコピーを取り出すこと.
これらはパフォーマンスを落とす原因・バグを生み出す原因でよく上げられる.
親クラスの変更に応じる場合のみnew修飾子を使用する
new修飾子を使用して,非virtualメソッドを再定義すると型の挙動が曖昧になる。
new修飾子は親クラスによって自身が作成したクラスと競合が起きた場合だけ使用する.
基本的にnew修飾子は使用しないほうがいい.