C#
作成日時:2024-08-29
更新日時:2024-11-16
LINQ
コレクションだったり、DB、XML等のデータを操作できる。
だいたいSQLみたいなことができる。
JavaのStreamみたいなやつ。
かつORMみたいなやつ。
Stringも実装している。IEnumerable<char>
target.all(c => Char.IsDigit(c))とか。
LINQ構文よりメソッド構文の方がやれること多い。
XXXOrDefault
単一値を取得する際はXXXOrDefault系のメソッドを使う。
XXXOrDefaultでないと、存在しなかった場合に例外を吐くから。
Blazor
リンク
.NET - Wikipedia
.NET Framework - Wikipedia
.NET Frameworkはメジャーアップデート終了。
Windows Server 2022のサポート期間(2031年10月14日)まではサポートが保証されるはず。
.NETが開発の推奨環境らしい。
Visual Studio Community 2022
License Terms | Microsoft Visual Studio Community 2022 - Visual Studio
会社での開発は基本ライセンス違反。(例外あり)
テスティングフレームワーク
- MSTest
- NUnit
- xUnit
言語バージョン
- 【C#】C#のバージョンと.NETのバージョンを確認する方法 #VisualStudio - Qiita
- Visual Studio 2019 で C# の言語バージョンを指定する - rksoftware
フォーマッタ―
.NET コード スタイル ルール オプション - .NET | Microsoft Learn
Visual Studio 2019 で一括フォーマットしたい #C# - Qiita
ルール
ツール>オプション>テキストエディタ
チーム
ツール>オプション>テキストエディタ
の各言語の「コードスタイル画面」から「.editorconfig」を出力できる。
内容は現時点で設定されているルール。
追加>新しいEditor Configでそれをインポートすれば、チーム内で共通のルールを使用可能。
メモ
- enumerable(イニューマラブル)
- アノテーション(属性)とリフレクションもあるよ
- Mutex/セマフォ
ソリューションファイル
- 複数のプロジェクトの管理
- プロジェクト間の依存関係の管理
- ソリューション全体のビルド
dotnet build ソリューションファイル
非同期
- asyncキーワードは、そのメソッドが非同期処理を「できる」ことを示すマーカー。awaitがなければ非同期にはならない。
- 非同期メソッドでも、その中に「awaitを使用した非ブロッキングAPIの呼び出し」が無ければ同期的、つまり単一スレッドで実行される。
- 非同期メソッドのawait以降の記述は、そのawaitを使用している非同期処理のコールバックのシンタックスシュガーと言える。
- 非同期メソッドでawaitが出たら、呼び出し元に戻ると考える。
- 非同期メソッドはTask<T>を返す。awaitはTaskから値を取り出す。
- メソッド名はXXXAsyncとかで。
async Task Method1() {
// something1とsomething2は並列で動く
var t1 = Task.run(() => {something1();});
something2();
await t1;
// something4はsomething3の完了後に動く
var t2 = await Task.run(() => {something3();});
something4();
// 親スレッドが先に終了する可能性がある
await Task.run(() => {something3();});
}
Entity Framework Core
- 直でSQLを書けると言えば書ける
- 匿名型を使えば、受け取るオブジェクトを定義しなくともよい
- ただし、結果をメソッドの戻り値にしたいならば、それを定義して詰める必要がある
例外
throw;
throw ex;
後者はスタックトレースが消える。
ヘルパーメソッド
ヘルパーメソッドを使えば、パフォーマンスの向上やバイトコードの削減がちょっと可能。
throw new InvalidOperationException("Some custom message from my library");
ThrowHelper.ThrowInvalidOperationException("Some custom message from my library");
そもそも例外使うな
しょうもないものは事前チェックで防いで、致命的なものをキャッチする。
TryParseやTryAddメソッドみたいな。
yamlとプロパティクラスの概念がある
Enum
Enumはあるけれども数値だけ。
JavaのEnumっぽく使う場合。
- Dictで保持したい内容を持っておく
- 属性 + リフレクション(または拡張メソッド)
- switch
- 通常のクラスを作って、定数クラスにする
定数
別アセンブリから使われないならconstでいい。
別アセンブリから使われるのならば、readonly static。
別アセンブリから使われる時にreadonly staticを使用するのは
フルビルドしないと定数の内容が別アセンブリに反映されないため。
constはコンパイル時定数、readonly staticは実行時定数。
イベント
- 発火できるのはそれを定義したクラスのみ
- 外部からはデリゲートの登録しかできない
ある意味、デリゲートをカプセル化したものと言える。
構造体
構造体は値型。
コレクションなどから取得する場合は、そのコピーが取得される。
public static void Main(string[] args)
{
var list = new List<Test>();
list.Add(new Test(1));
var t = list[0];
t.num = 10;
Console.WriteLine(t.num);
Console.WriteLine(list[0].num);
}
// Testがクラスの場合は、どちらも10
// Testが構造体の場合は、10, 1
参照型の退避
var a = b;
// ここのタイミングでbが別スレッドなどから書き換えられた場合、挙動が変わる。nullとかならエラーになる。
b.XXX();
// aに一旦退避することで、bの参照が書き換えられようとも、対比した時点のインスタンスでメソッドを実行。
a.XXX();
// null回避だけならこれでも可
b?.XXX();
キャスト
as演算子を使う。
失敗しても例外は出ず、nullが格納される。
object s = "123";
var i = s as List<string>;
//var i = (List<string>)s; // 例外発生(System.InvalidCastException)
Console.WriteLine(i == null);
コードメトリクス
分析→コードメトリックスを計算する。
でソースの分析をしてくれる。
正規表現
Regexクラスの静的メソッドを使え。
正規表現をキャッシュしてくれるから、パフォーマンスが上がる。
遅延評価
メモリ使用量の削減とか。
getでもいいが、lockを使ってスレッドセーフにする必要がある。
// 値を取得する処理を登録
// 第2引数はLazyThreadSafetyMode列挙型
// デフォルト引数のExecutionAndPublicationはスレッドセーフらしいのでこのままで良いかも
var lazy = new Lazy<string>(() => "");
// 使用時に初めて値を取得する処理が動く
var val = lazy.Value;
// IsValueCreatedで値が取得済みかを判定
if (lazy.IsValueCreated){}
// 個々では取得済みの値が返却。
val = lazy.Value;
スレッドセーフ
lock用のオブジェクトを使ってロックした方がいいらしい。
private readonly object _lock = new object();
public void SomeMethod()
{
lock(_lock)
{
// スレッドセーフな処理
}
}
lock(this)はよくないらしい。
- オブジェクト全体をロックするため、粒度が粗い
- 予期せぬデッドロックのリスク
- クラスの外部からもロックできてしまう可能性
Javaにも言える。
regionプリプロセッサディレクティブ
使ったら負けだと思え。
要するにメソッドやクラスが適切に分割されていない可能性がある。
めちゃくちゃ長いSQLとかリテラルとかを隠す場合のみ使っていい。
本当に仕方がない時だけ使う
クソコードをリファクタリングする過程で仕方なくとかね。
メソッド拡張
既存のクラスにメソッドを追加できる。
静的メソッドのみ。
static class SampleExtensions
{
public static void Method(this string str)
{
}
}
バリデーション
デフォであるのは下記。
ライブラリ(FluentValidation)も色々ある。
using System.ComponentModel.DataAnnotations;
public class User
{
[Required(ErrorMessage = "名前は必須です")]
[StringLength(50, MinimumLength = 2, ErrorMessage = "名前は2〜50文字である必要があります")]
public string Name { get; set; }
[Range(18, 100, ErrorMessage = "年齢は18〜100歳である必要があります")]
public int Age { get; set; }
[EmailAddress(ErrorMessage = "メールアドレスの形式が正しくありません")]
public string Email { get; set; }
// バリデーションの手動実行
public bool Validate()
{
var context = new ValidationContext(this, serviceProvider: null, items: null);
var results = new List<ValidationResult>();
return Validator.TryValidateObject(this, context, results, validateAllProperties: true);
}
}
列挙型
Javaほど柔軟ではない。数値の列挙という感じ。
複数の属性(メンバ)を持つ列挙型を使いたければ、下記のいずれか
- enumではなくImmutableなクラスを作って、Javaの列挙型の如く使う
- メンバにreadonlyなインスタンスを生成して、それを渡すなど
- enumを使用するが、staticなDictionary<enumのキー, 保持したい値>を内部に持ち、それを使用
- staticなDictionaryは静的コンストラクタで初期化
- もしくはメソッドを作って、switch式で返すとか
public enum PaymentMethod
{
CreditCard,
DebitCard
public decimal GetTransactionFee() => this switch
{
CreditCard => 0.03m,
DebitCard => 0.01m,
_ => throw new ArgumentException()
};
}
ref
若干異なるがポインタみたいな感じの解釈で。
static ref string Ref(ref string obj) // 1
{
obj = ""; // 2
Console.WriteLine(obj.ToString()); // 3
return ref obj; // 4
}
- 実引数のstringのアドレスが格納されている場所のアドレスを渡している。C言語っぽくすると(string** obj)みたいな感じ。
- アドレスに別のstringのアドレスを書き込む。実引数のstringもそれを参照する。
- 取得系は暗黙的にアスタリスクで実体を取得して実行していると考える。(*obj).ToString()みたいな感じ。
- &(*obj)つまりobj。stringインスタンスのアドレスを格納している場所のアドレスを返している。
ref / out / in
違うけどニュアンスはこんな感じか。
- ref:ポインタ
- out:書き込み用ポインタ
- in:読み込み用ポインタ
正直in以外使いたくない。(大きいサイズの構造体を渡すときだけに使う。)
値の変更などは戻り値のみで表現すべきだと思う。
タプルとか使ってさ。
outはTryXXX系で使うかもしれないが。
Visual Studio
- Ctrl + K → Ctrl + Dで全体フォーマット
- Ctrl + K → Ctrl + Fで選択部フォーマット
- F12で定義に移動
- 矩形選択はAlt
匿名型とタプル
便利。
コレクションのキャスト
JavaでArrayListをListにキャストするが如く。
目的
- 依存性逆転の原則
- 多態性
- 機能の制約を付与
選択肢(Listの場合)
- IList
- リストの要素を追加、削除、インデックスによるアクセスが必要な場合。
- ICollection
- 要素の数や包含チェックなど、基本的なコレクション操作が必要な場合。
- IEnumerable
- foreach文やLINQを使用してデータを繰り返し処理する場合。
- IReadOnlyList
- データの変更を防ぎ、読み取り専用のリストとして扱う場合。
共変
総称型G(T)において型Xを型Yに変換できる際、G(X)がG(Y)に変換できる場合、
G(T)は共変性を持つという。
配列は共変性を持つ。
総称型は定義時にin/outを指定することで、反変(contravariant)/共変(covariant)を設定できる。
※インターフェイスとデリゲートのみ定義可能
object[] o = new string[3];
interface IProducer<out T> {}
interface IConsumer<in T> {}
ニュアンスはoutが上限付きワイルドカード。inが下限付きワイルドカード。
簡易チートシート
#region プリプロセッサディレクティブ
// シンボルの定義
// #define symbol1
// 【プロジェクト全体にシンボルを適用する場合】
// プロジェクトのプロパティ→「条件付きコンパイル シンボル」で検索
// もしくはプロパティのXMLに直で記載
#if !DEBUG
// エラーや警告
#warning デバッグモードじゃないですよ。
// symbol2が定義されていれば、ここに書いてあるコードはコンパイル対象となる。
Console.WriteLine("call");
#endif
#endregion
// Javaで言うimport。
// 型インポートもある。using static xxx
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Runtime.CompilerServices;
// 基本的にクラス、プロパティ、定数、メソッドはパスカルケース
// 名前空間
// 企業名.製品名.機能名など任意
// C# 10から名前空間に{}が要らなくなった
namespace CompanyName.Tech.Id.Any;
/// <summary>
/// クラスの説明を書く
/// </summary>
internal class SampleClass : Object
{
public static void Main(string[] args) => SampleClass.LinqSample();
// 【定数】前者はビルド時に評価、後者は実行時に評価
public const int Constraint = 0;
public static readonly int ReadOnly = 1;
#region プロパティサンプル
public int Prop1 { private get; set; }
public int Prop2 { get; init; } // initはコンストラクタかオブジェクト初期化子のみ設定可能
public int Prop3 { get; } = 1;
private int _prop4 = 10;
public int Prop4
{
get => _prop4;
internal set => _prop4 = value;
}
#endregion
// デリゲート。関数ポインタとか関数インターフェース的なもの。
// 実際のメソッド、匿名メソッド、ラムダ式で登録したり。
// クロージャもできる。(外部変数のキャプチャ)
// +/-で複数の処理を登録/削除出来たりする。(マルチキャストデリゲート)
// 戻り値は最後に登録された処理のものになる。
// もちろん引数にも型にでも何でも出来る。
private delegate void DelegateSample(int num);
// インデクサ:外部から配列の様にアクセスできる
// obj[1]とか
public int this[int index]
{
set
{
// checkedはオーバーフロー時に例外(OverflowException)を出す
// 設定ファイルでアプリ全体にcheckedをつけることも出来る
checked { _prop4 = value++; }
}
get { return 0; }
}
/// <summary>
/// コンストラクタ
/// </summary>
SampleClass() : base()
{
// 親のコンストラクタや、自クラスのコンストラクタのオーバーロードを呼ぶ場合
// メソッド定義の横に記載
// 親のメソッドを読んだり
Console.WriteLine(base.ToString());
}
// 静的コンストラクタ
static SampleClass()
{
// staticイニシャライザ的な物
}
[ModuleInitializer]
public static void Init()
{
// アセンブリが読み込まれた時に発火
}
// override:親クラスのvirtualメソッドをoverride
// sealed:子クラスでoverrride不可
// メソッド単位のsealedはoverrideとセットか?
public sealed override string? ToString()
{
return base.ToString();
}
/// <summary>
/// メソッドの要約を書く
/// </summary>
/// <param name="arg">引数の説明を書く</param>
/// <returns>戻り値の説明を書く</returns>
internal static (string, int) Sample(int arg, int option = 1)
{
// オブジェクト初期化子
new SampleClass() { Prop1 = 1 };
// 絵文字やサロゲートペアの文字数には気をつけろ
Console.WriteLine("🚗".Length); // 2
Console.WriteLine(new StringInfo("🚗").LengthInTextElements); // 1
// 文字列比較は、デフォで大文字小文字を区別している
// 匿名型(変更不可かつ参照型。LINQとかで使う)
var anon = new { name = "名前", age = 32 };
// タプルは型なのでどこにでも書ける
// 変数の型やメソッドの戻り値や引数
// var (val1, val2, _) = getTuple(); 的に受け取ったり(アンダーバーは要らないとき使う)
return ("", 1);
}
protected virtual void VariableSample(string _) // 引数の破棄。ラムダやswitch等でも使える。
{
// ビルトイン整数型は符号有無あり。先頭にu。
// byteは無印が符号なし、符号ありはubyte
// 型推論あり。基本キャメルケース
// リテラルはJavaに似ている。符号なしは先にuをつける(100uL=unsigned long)
var v = 15;
// 変換系はConvertクラスにある
string c = Convert.ToString(v, 16);
// null許容(参照)型 / null合体演算子 / null条件演算子
// null許容型のプロパティにはhasValueとValueがある。
// →前者はnullかどうかの判断。後者はnullだと例外を発生させる。
string? d = null;
d = null ?? c?.ToString(); // null合体代入というのもある。d ??= c?.ToString();
// 逐語的文字列リテラルと変数展開
Console.WriteLine($@"{v}\ {^1}");
}
private static void ArraySample()
{
// 配列
// 「^」はIndex。「..」はRangeを生成する
// 数値は整数型の変数に置き換え可能
Index idx = ^1; // 「x^y 」は論理演算やビット演算になる
Range rng = 1..2;
var array = new int[] { 1, 2, 3 };
var a1 = array[idx]; // 後方からm番目。3
var a2 = array[rng]; // [2]
var a3 = array[..2]; // [1, 2]
// 多次元配列
var multiArray = new int[1, 2];
// ジャグ配列
var jaggedArray = new int[2][];
jaggedArray[0] = new int[5];
jaggedArray[1] = new int[3];
// 配列の比較
array.SequenceEqual(a2);
}
private static void ControlStatementSample()
{
// ifや繰り返し系はだいたいJavaと同じ
// foreach
foreach (var i in Enumerable.Range(0, 5)) { }
// switch
// 基本Javaと同じ。型判定や条件式が追加できる。
object obj = 1;
switch (obj)
{
case 1:
break;
// 処理が無ければフォールスルーできる
case int i when i > 10:
case var o:
default:
break;
}
// switch式
var a = obj switch
{
int i when i > 10 => "10より大きい数値",
> 5 and < 8 => "5より大きく8より小さい",
_ => "デフォルト"
};
}
// 可視性を設定しないとprivate
// BOMやバッファーサイズに注意
static void FileSample()
{
try
{
// using。Javaのtry-with-resourceみたいな
// IDisposableインターフェース
using (var writer = new StreamWriter(@"ファイルパス"))
using (var reader = new StreamReader(@"ファイルパス"))
{
writer.WriteLine("書き込み");
reader.ReadLine();
}
// こっちでもスコープを抜ければ解放される
using var writer2 = new StreamWriter(@"ファイルパス");
using var reader2 = new StreamReader(@"ファイルパス");
}
catch (Exception ex) when (ex.InnerException != null)
{
// throw ex;だとスタックトレースがここ起点になってしまう
throw;
}
catch (Exception ex) when (ex is FieldAccessException || ex is OutOfMemoryException)
{
throw new Exception();
}
finally { }
}
protected void CollectionSample()
{
// Javaと一緒で、初期容量を指定できる。
var list = new List<int>() { 1, 2, 3 };
list.Add(1);
var dict = new Dictionary<int, int>()
{
[1] = 2,
[2] = 3,
};
// Addは既にキーが居たら例外。
// TryAddはキーが居たら何もしない
dict.TryAdd(1, 20);
// こっちの形式が安全っぽい
// JavaのいつものMapみたいな感じ
dict[1] = 3;
foreach ((var key, var val) in dict)
{
Console.WriteLine($"{key}, {val}");
}
// 読み取り専用
var readOnlyList = new ReadOnlyCollection<int>(list);
// イミュータブル。Addとかすると新しいインスタンスができて、元のリストは不変
var immutableList = ImmutableList.Create(list);
// マルチスレッド対応
var concurrentList = new ConcurrentBag<int>(list);
}
// staticクラスはインスタンス生成不可かつ継承不可
public static class Util
{
// 参照渡し
// ポインタだと思えばいい
public static void SomeMethod()
{
var i = 50;
SomeMethod(ref i);
Console.WriteLine(i); // 10
var rng = new Range(1, 2);
var str = "1";
ref var ret = ref ReadOnlyRef(rng, ref str);
ret = "123";
Console.WriteLine($"{str}, {ret}"); // 123, 123
var num = 10;
OutSample(out num);
Console.WriteLine(num); // 50
}
// 値型の参照渡しと可変長引数
static void SomeMethod(ref int num, params string[] options)
{
num = 10; // 呼び出し元のnumに影響を与える
}
static ref string ReadOnlyRef(in Range rng, ref string obj)
{
// 構造体などは値渡しなので、全てが複製される
// パフォーマンスが悪いので、構造体の参照を渡すのがin
// 構造体のメンバは更新できない
Console.WriteLine(rng.Start);
return ref obj;
}
// out:出力引数
// TryXXX系を実装するときなど
// GoのカンマOKイディオム的な思想もありそう
static bool OutSample(out int num)
{
num = 50;
return true;
}
}
private struct StructSample
{
public readonly string str;
public int num;
public int val;
public StructSample() { str = "1"; num = 2; }
public static void Sample()
{
var a = new StructSample { num = 20 };
// 値型なのでコピーされる。参照型と違い、別のもの。
// ただしシャローコピーなので、参照型メンバは同じインスタンスを見ている。
// 副作用を持つメソッドを呼んだらコピー元にも影響が出る可能性がある。
var b = a with { val = 3 }; // with式。コピーしたうえで値設定。レコードなどでも使える。
b.num = 200;
Console.WriteLine($"{a}, {b}"); // 20, 200
}
public override string? ToString() => num.ToString();
}
// レコード。継承もできる。
// record 参照型。基本イミュータブル
// record struct。レコード構造体。値型。ミュータブルなのでreadonlyをつける。
public readonly record struct RecordSample(string val);
// Objectを継承したジェネリック型で、その型パラメータはIDisposableとIEquatableを実装している
// 型パラメータの制約はいろいろ指定可能
internal class GenericSample<T, U> : Object where T : IDisposable, IEnumerable<T>
{
public readonly T? _val = default(T); // ジェネリック。型が不明ならばdefaultでデフォ値を設定できる。
// ジェネリックメソッド
public T GenericMethod<T>() => default(T);
}
// インターフェース/デリゲートのみ。
// T派生と、U上位のクラスを認める
internal interface GenericInterface<out T, in U>;
public static void LinqSample()
{
var list = Enumerable.Range(0, 5); // データソースはIEnumerable等を実装している。
var s = list
.Where(b => b > 1)
.Select(b => new { v = b.ToString() }); // 匿名型
//.ToArray();
// LINQは遅延実行。使用する際に初めて評価される。
// 即時で欲しければToArray()等を呼ぶ。
foreach (var str in s)
{
Console.WriteLine(str);
}
}
}