セージ の メモ書き

メモこそ命の恩人だ

PostgreSQL - セットアップ

PostgreSQL

https://www.postgresql.jp/


セットアップ

  • インストーラーをダウンロード。

  • "postgresql-14.4-1-windows-x64.exe" を実行。

  • デフォルトのまま "Next" を選択。

    • コンポーネントは全てインストールする。
      • PostgreSQL Server:DBMS 本体
      • pgAdmin 4:DBMS の管理ツール。GUI ツール
      • Stack Builder:周辺ツールをインストールするユーティリティ
      • Command Line Tool:CUI ツール。

  • パスワードを入力。(例として「1234」を入力)

  • デフォルトのまま "Next" を選択。

    • ポートは "5432" がデフォルト。
    • Locale は SQL Server の照合順序のようなもの。

  • インストールを行う。1分ほどで完了。最終画面のチェックは外して "Finish" を選択。


pgAdmin4

  • PostgreSQL 用の GUI ツール。
  • テーブル作成やCRUD操作などを行える。
  • 前述の手順でインストールされる。

起動

  • 前述で設定したパスワードを入力。

DBの確認

  • Servers → PostgreSQL* → Databases を選択。
  • postgres が既定のDB。

テーブル作成

  • postgresDB → Schemas → Tables を選択。
  • 右クリック、Create → Table を選択。
  • "General" でテーブル名を入力。
  • "Columns" でカラム情報を入力。

SQL 実行

  • テーブルを選択し、"Scripts" から目的のスクリプトを選択。

  • SQLを記入後、実行。

  • 表マークのボタンからテーブル内容を確認できる。



以上

C# - null 許容参照型

null 問題

アントニー・ホーア - Wikipedia

  • アントニー・ホーア氏が null 参照を発明。
  • 2009年のカンファレンスにて、null 参照を発明したことを謝罪。

それは10億ドルにも相当する私の誤りだ。null参照を発明したのは1965年のことだった。当時、私はオブジェクト指向言語 (ALGOL W) における参照のための包括的型システムを設計していた。目標は、コンパイラでの自動チェックで全ての参照が完全に安全であることを保証することだった。しかし、私は単にそれが容易だというだけで、無効な参照を含める誘惑に抵抗できなかった。これは、後に数え切れない過ち、脆弱性、システムクラッシュを引き起こし、過去40年間で10億ドル相当の苦痛と損害を引き起こしたとみられる。

null 安全

  • null 例外にならない仕組み。
  • 近年の言語は null 安全が考慮されている。

null 許容参照型

null 許容参照型 | Microsoft Docs

  • C# 8.0 以降で使用できる。
  • null 参照の例外を最小限に抑えることができる。
  • "NULL 許容" を "無効" にすれば、null を無効化できる。
  • ? で明示的に null 許容型にすれば、null を代入できる。
public class SampleClass
{
    public int Value { get; set; }
}
SampleClass sample;
// Debug.WriteLine(sample.Value);
// ビルドエラーになる。インスタンス生成していないため、検知してくれる。
SampleClass sample;
sample = new SampleClass();
Debug.WriteLine(sample.Value);
// インスタンス生成すれば、ビルドが通ることを確認。
SampleClass sample;
// SampleMethod(sample);
// ビルドエラーになる。インスタンス生成していないため、検知してくれる。

void SampleMethod(SampleClass sample) 
{
    Debug.WriteLine(sample.Value);
}

ただし、参照型の定義をネストすると、検知できなかった。
完全に null 安全というわけではなさそう...

public class SampleClass1
{
    public SampleClass2 sample2 { get; set; }
}
public class SampleClass2 { }
SampleClass1 sample1;
sample1 = new SampleClass1();
Debug.WriteLine(sample1.sample2.ToString());
// 例外発生:System.NullReferenceException: 'Object reference not set to an instance of an object.'

// プロパティは宣言時に初期化した方が安全。



以上

C# - global using ディレクティブ

global using ディレクティブ

docs.microsoft.com

  • global 修飾子を using ディレクティブに追加すると、該当プロジェクトの全ファイルに using が適用される。
  • global 修飾子の使用ルールを決めた方が良い。"エントリーポイントで使用する"など。
  • 利点
    • 頻繁に使用する using が記述不要になる。(共通ライブラリ etc.)
    • using の割れ窓状態を改善できる。(並び替え、未使用 etc.)

Debug クラスを使用するため、"global using System.Diagnostics;" を指定する。

global using System.Diagnostics;

namespace ConsoleApp1
{
    internal class Class1
    {
        public void Sample1()
        {
            Debug.WriteLine("お試し");
        }
    }
}

using なしで、Debug クラスを使用する。

// using System.Diagnostics;
// コメントを外してもビルドエラーにならないことを確認。
// "using ディレクティブは必要ありません" と表示される。

namespace ConsoleApp1
{
    internal class Class2
    {
        public void Sample2()
        {
            Debug.WriteLine("お試し");
            // using System.Diagnostics; なしで使用できることを確認。
        }
    }
}


暗黙的な global using

docs.microsoft.com

  • global using を暗黙的に適用できる。
  • 方法
    • プロジェクトのプロパティで "暗黙的な global using" を有効にする。
  • フレームワークごとに global using の内容が異なる。
    • obj フォルダーに "プロジェクト名.GlobalUsings.g.cs" が生成される。
    • この中に global using に定義されている。

コンソール

// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;

ASP.NET MVC

// <auto-generated/>
global using global::Microsoft.AspNetCore.Builder;
global using global::Microsoft.AspNetCore.Hosting;
global using global::Microsoft.AspNetCore.Http;
global using global::Microsoft.AspNetCore.Routing;
global using global::Microsoft.Extensions.Configuration;
global using global::Microsoft.Extensions.DependencyInjection;
global using global::Microsoft.Extensions.Hosting;
global using global::Microsoft.Extensions.Logging;
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Net.Http.Json;
global using global::System.Threading;
global using global::System.Threading.Tasks;

WPF

// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.Linq;
global using global::System.Threading;
global using global::System.Threading.Tasks;

Form

// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.Drawing;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;
global using global::System.Windows.Forms;



以上

C# - バイト配列の操作

概要

データ参照

  • 3パターンの参照方法をメモ。
  • ArraySegment 構造体が最速。

以下のデータを参照する。

var data = Enumerable.Range(0, 100).Select(x => (byte)x).ToArray();

このメソッドで取得データを確認。

void Log(byte[] byteArray) 
{
    byteArray.ToList().ForEach(x => Debug.Write($"{x}, "));
    Debug.WriteLine($"");
}

ArraySegment 構造体

docs.microsoft.com

  • 配列の指定インデックスから指定サイズ分のデータを取得する。
  • バイト配列以外でも使用OK。
  • byte[] で拡張メソッドとして定義しておくと良い。
var result = GetPartialByteArray(data, 10, 10);
Log(result);
// 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 

byte[] GetPartialByteArray(byte[] byteArray, int index, int size)
{
    if (byteArray == null) return Array.Empty<byte>();
    return new ArraySegment<byte>(byteArray, index, size).ToArray();
}

範囲指定のインデックス

docs.microsoft.com

  • C# 8.0 以降で使用できる。
  • n..m の形式で範囲指定する。(n番目~m番目)
  • ^n で末尾からの指定もできる。
Log(data[10..20]);
// 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 

Log(data[95..^0]);
// 95, 96, 97, 98, 99, 

Log(data[95..^1]);
// 95, 96, 97, 98, 

Skip・Take メソッド

  • LINQ の組合せでも参照できる。
  • データ量が多いと低速になるので注意。
var result = GetPartialByteArray(data, 10, 10);
Log(result);
// 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 

byte[] GetPartialByteArray(byte[] byteArray, int index, int size)
{
    if (byteArray == null) return Array.Empty<byte>();
    return byteArray.Skip(index).Take(size).ToArray();
}


データ作成

  • "yield return"、"SelectMany" などにより簡潔に記述できる。
var result = CreateValue("ABC").SelectMany(x => x).ToList();
result.ToList().ForEach(x => Debug.Write($"{x}, "));
// 2, 65, 66, 67, 3, 

IEnumerable<IEnumerable<byte>> CreateValue(string sample)
{
    yield return GetStx();
    yield return GetSample();
    yield return GetEtx();

    IEnumerable<byte> GetStx()
    {
        yield return 0x02;
    }
    IEnumerable<byte> GetSample()
    {
        return Encoding.UTF8.GetBytes(sample);
    }
    IEnumerable<byte> GetEtx()
    {
        yield return 0x03;
    }
}



以上

C# - init 専用セッター

init 専用セッター

docs.microsoft.com

public 型 プロパティ名 { get; init; }
  • C# 9.0 以降で使用できる。
  • プロパティの初期化をオブジェクト生成時に制限できる。
  • get プロパティのみが最も制限が強い。
  • init 専用セッターにより、制限が若干緩和。
  • レコード型で with 式が使用可能になる。
  • レコード型の既定のプロパティとして使用される。
public class SampleClass
{
    public int Value { get; init; } 
}
var sample = new SampleClass() { Value = 10 };  // 生成時は初期化できる。
Debug.WriteLine(sample.Value);
// 10

// sample.Value = 20;
// ビルドエラーになることを確認。オブジェクト生成後は、変更不可。

初期化の順序

  • 順序1:宣言時
  • 順序2:コンストラク
  • 順序3:オブジェクト生成時のプロパティ
public class SampleClass
{
    public SampleClass() { }

    public SampleClass(int value)
    {
        Value = value;  // 順序2
    }

    public int Value { get; init; } = 1;   // 順序1
}
var sample1 = new SampleClass();
Debug.WriteLine(sample1.Value);
// 1

var sample2 = new SampleClass(5);
Debug.WriteLine(sample2.Value);
// 5

var sample3 = new SampleClass(5) { Value = 10 };
Debug.WriteLine(sample3.Value);
// 10



以上

C# - レコード型

レコード型

docs.microsoft.com

// 形式1(位置指定構文)
public record レコード名(型 プロパティ1, 型 プロパティ2, ...);

// 形式2(位置指定構文+追加定義)
public record レコード名(型 プロパティ1, 型 プロパティ2, ...)
{
   // 追加定義
}

// 形式3(クラスや構造体と同じ)
public record レコード名
{
   // 定義
}
特徴 備考
参照型 class と同様
継承できる class と同様
値ベースで比較できる struct と同様
変更不可 init アクセサの機能
with で複製できる record 専用の機能
  • C# 9.0 以降で使用できる。
  • イミュータブルなオブジェクトを簡潔に記述できる。
  • 以下が自動生成されるため簡潔になる。
    なので、クラスでも同様の表現はできる。
    • init 専用セッター
    • Equals メソッド
    • GetHashCode メソッド
    • == 演算子、!= 演算子
    • ToString メソッド
  • 以下、構造体のデメリット(Microsoft docs)
    • 継承できない。
    • 値の等価判定が効率的でない。
    • 値のコピーに多くのメモリが使用される。
  • このデメリットを record 型では改善できる。
    • 継承OK。
    • 等価判定、値コピーの方法が改善。
  • "record class" と宣言してもOK。(C# 10 以降)
    • "record struct" が登場したため。
    • つまり、record はクラスよりの概念? 参照型だし。

レコード型で "名前" オブジェクトを定義してみる。

public record Name(string FirstName, string LastName);

クラスで表現するなら、こんな感じ。この自動生成は有難い。

public class Name : IEquatable<Name?>
{
    // コンストラクタでの値代入
    public Name(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }

    // init 専用セッター
    public string FirstName { get; init; }
    public string LastName { get; init; }

    // 等価判定
    public bool Equals(Name? other) => other != null && 
        FirstName == other.FirstName && LastName == other.LastName;
    public override bool Equals(object? obj) => Equals(obj as Name);
    public override int GetHashCode() => HashCode.Combine(FirstName, LastName);
    public static bool operator ==(Name? left, Name? right) => EqualityComparer<Name>.Default.Equals(left, right);
    public static bool operator !=(Name? left, Name? right) => !(left == right);

    // ToString
    public override string ToString()
    {
        // ... プロパティの内容を JSON 形式っぽく出力 ...
        return "型名 { プロパティ1: 値, プロパティ2: 値, ... }";
    }
}


↑のレコード型について、自動生成機能が動作するか試してみる。

// ★レコード型のオブジェクト
var data = new Name("一郎", "鈴木");

// data.FirstName = "田中";
// init 専用セッターのため、ビルドエラーになることを確認。

Debug.WriteLine($"{data.FirstName} {data.LastName}");
// 鈴木 一郎
// プロパティを参照できることを確認。

var data2 = new Name("一郎", "鈴木");
var data3 = new Name("三郎", "鈴木");
Debug.WriteLine(data.Equals(data2));
// True
Debug.WriteLine(data.Equals(data3));
// False
// 等価判定が正しく動作することを確認。

Debug.WriteLine(data.GetHashCode());
// 233721806
// ハッシュ値を取得できることを確認。

Debug.WriteLine(data.ToString());
// Name { FirstName = 一郎, LastName = 鈴木 }
// レコード型の形式で出力できることを確認。

継承

  • レコード型同士の場合、継承できる。
  • クラスとレコード型の組合せは不可。
public record SampleRecord1(int Value1);
public record SampleRecord2(int Value1, int Value2) : SampleRecord1(Value1);
// レコード型同士で継承できることを確認。

//public class SampleClass : SampleRecord1(Value1: 0)
//{
//}
// ビルドエラーになることを確認。クラスの継承元にレコードは使用できない。

public class SampleClass { }
//public record SampleRecord3(int Value1): SampleClass;
// ビルドエラーになることを確認。レコード
// の継承元にレコードは使用できない。

値オブジェクト

  • 値オブジェクトは DDD の考え方。
    • 識別子で管理しない不変なオブジェクト。
    • 識別子で管理する場合は、エンティティ。
  • レコード型での問題点
    • 位置指定構文の場合、引数チェックできない。
    • なので、完全コンストラクタで扱えない。
  • レコード型での対応方法
    • プロパティを定義(init 専用セッターを使用)
    • コンストラクタを定義し、引数をチェック
  • 備考
    • そのうち、上記処理を行える機能が追加されるといいな。
public record Name
{
    public Name(string firstName, string lastName)
    {
        if (string.IsNullOrEmpty(firstName)) throw new ArgumentNullException(nameof(firstName));
        if (string.IsNullOrEmpty(lastName)) throw new ArgumentNullException(nameof(lastName));

        const int maxLength = 10;
        if (firstName.Length > maxLength) throw new ArgumentOutOfRangeException(nameof(firstName));
        if (lastName.Length > maxLength) throw new ArgumentOutOfRangeException(nameof(lastName));

        FirstName = firstName;
        LastName = lastName;
    }

    public string FirstName { get; init; }
    public string LastName { get; init; }
}

with 式

docs.microsoft.com

コピー先 = コピー元 with { プロパティ名 = 値, ... }
  • レコード用の機能。
  • オブジェクトを部分的にコピーできる。
  • ディープコピーではない。参照型メンバーの場合、参照のコピー。
public record SampleRecord(int IntValue, SampleClass ClassValue);

public class SampleClass
{
    public int Value { get; set; }

    // 中身を確認しやすいように出力
    public override string? ToString() => Value.ToString();
}
var sample1 = new SampleRecord(10, new SampleClass() { Value = 100 });
var sample2 = sample1 with { IntValue = 20 };

Debug.WriteLine(sample1.ToString());
// SampleRecord { IntValue = 10, ClassValue = 100 }
Debug.WriteLine(sample2.ToString());
// SampleRecord { IntValue = 20, ClassValue = 100 }
// IntValue のデータのみ変更できたことを確認。

sample1.ClassValue.Value = 200;
Debug.WriteLine(sample1.ToString());
// SampleRecord { IntValue = 10, ClassValue = 200 }
Debug.WriteLine(sample2.ToString());
// SampleRecord { IntValue = 20, ClassValue = 200 }
// 参照型メンバーは参照のコピーのため、sample1 と sample2 で同じ値になることを確認。



以上

C# - is 演算子

docs.microsoft.com

型判定

  • 型が一致する場合、true を返す。
  • 基底クラスの場合、true と判定される。
    • アップキャストできるか判定できる。
  • インスタンスの中身が null でも例外にならない。
public class SampleBaseClass { }
public class Sample1Class : SampleBaseClass { }
public class Sample2Class : SampleBaseClass { }
var input = new Sample1Class();
Debug.WriteLine(input is SampleBaseClass);
// True
Debug.WriteLine(input is Sample1Class);
// True
Debug.WriteLine(input is Sample2Class);
// False

input = null;
Debug.WriteLine(input is Sample1Class);
// False

キャスト

  • 型判定し true である場合、その型のインスタンスを生成する。
  • "キャスト" と "null チェック" をまとめて記述できる。
  • C# 7.0 以降で使用できる。
var input = new Sample1Class();

if (input is Sample1Class castedValue)
{
    // is 演算子 の判定が true の場合の処理
}
else
{
    // is 演算子 の判定が false の場合の処理
}

従来の場合、こんな感じ。

var input = new Sample1Class();

var castedValue = input as Sample1Class;
if (castedValue != null)
{
    // キャストに成功した場合の処理
}
else
{
    // キャストに失敗した場合の処理
}

パターンマッチング

パターン - C# リファレンス | Microsoft Docs

論理パターン(and/or/not)

  • 値・型のどちらの判定でも is 演算子は使用できる。
  • && や || などの演算子より簡潔に記述できる。
    • 1つの変数に対して判定文を記述できるため。
// 値の判定
int input = 10;

Debug.WriteLine(input is (1 or 5 or 10));
// True

Debug.WriteLine(input is (>= 1 and <= 10));
// True

Debug.WriteLine(input is (> 1 and < 10));
// False
// 型の判定
void Sample<T>(T input) => Debug.WriteLine(input is float or double);

Sample(1.0);
//True

Sample(1);
//False
// null の判定
int? input = null;

Debug.WriteLine(input is null);
// True

Debug.WriteLine(input is not null);
// False

プロパティパターン

  • C# 8.0 以降に使用できる。
  • プロパティの値を判定できる。
  • 論理パターンと併用できる。
var input = new DateTime(2022, 6, 18);

Debug.WriteLine(input is { Year: 2022 });
// True
Debug.WriteLine(input is { Year: 2022, Month: 6 });
// True
Debug.WriteLine(input is { Year: 2022, Month: 4 or 5 or 6 });
// True

Debug.WriteLine(input is { Year: 2022, Month: 1 });
// False
Debug.WriteLine(input is { Year: 2022, Month: 1 or 2 or 3 });
// False

リストパターン

  • C# 11 以降で使用できる。
  • ToDo:環境がないので、今度試す。



以上

C# - switch 式

switch 式

docs.microsoft.com

x switch { ... };
  • C# 8.0 以降で使用できる。
  • switch 文よりも簡潔に書ける。case・break 不要。
  • 判定機能も従来よりかなり充実してる。おすすめ。
  • アンダーバー"_" が default と同じ。
  • 複数行の処理を行う場合、使用できない。

簡潔に記述できる。

enum SampleEnum
{
    Type1,
    Type2,
    Type3,
}
var input = SampleEnum.Type1;
var result = input switch
{
    SampleEnum.Type1 => "タイプ1",
    SampleEnum.Type2 => "タイプ2",
    SampleEnum.Type3 => "タイプ3",
    _ => throw new ArgumentException(input.ToString()),
};
Debug.WriteLine(result);
// タイプ1

switch 文だと、短く書きたくてもこれぐらい。ローカル関数などが必要。

var input = SampleEnum.Type1;
var result = GetType(input);
Debug.WriteLine(result);
// タイプ1

string GetType(SampleEnum type) 
{
    // 簡潔に記述できない。。。
    switch (type)
    {
        case SampleEnum.Type1: return "タイプ1";
        case SampleEnum.Type2: return "タイプ2";
        case SampleEnum.Type3: return "タイプ3";
        default: throw new ArgumentException(type.ToString());
    }
}

switch 式は、一行の場合に使用できる。

var input = SampleEnum.Type1;
var result = input switch
{
    // 複数行の場合、ビルドエラー。
    SampleEnum.Type1 => 
    {
        var dummy = 0;
        return "タイプ1";
    },
    SampleEnum.Type2 => "タイプ1",
    SampleEnum.Type3 => "タイプ1",
    _ => throw new ArgumentException(input.ToString()),
};

{ } 判定

  • null 以外のデフォルト処理を定義できる。
  • null は アンダーバー (_) で処理される。
void Sample(SampleEnum? input) 
{
    var result = input switch
    {
        SampleEnum.Type1 => "タイプ1",
        { } => "タイプ2 or タイプ3",
        _ => "null",
    };
    Debug.WriteLine(result);
}
Sample(SampleEnum.Type1);
// タイプ1

Sample(SampleEnum.Type2);
// タイプ2 or タイプ3

Sample(null);
// null

比較演算判定

  • C# 9.0 以降は、比較演算子も使用できる。
var input = 10;
var result = input switch
{
    0  => "0です",
    > 0 => "正の数",
    < 0 => "負の数",
};
Debug.WriteLine(result);
// 正の数
var result = input switch
{
    0 => "0です",
    1 => "1です",
    2 => "2です",
    10 => "10です",
    _ => throw new ArgumentException(input.ToString()),
};
Debug.WriteLine(result);
// 10です

プロパティ判定

  • プロパティを部分的に比較判定できる。
  • 複数のプロパティで判定することもできる。

以下のクラスを使用する。

public class SampleClass1
{
    public int Property1 { get; set; }
    public string Property2 { get; set; }
    public bool Property3 { get; set; }
}

プロパティを自由に比較できる。これスゴイ。

public void Sample(SampleClass1 value) 
{
    var result = value switch
    {
        { Property1: 1, Property2 : "Test",  Property3: true } => $"(^^)/",
        { Property1: 1 } => $"1です",
        { Property2: "Test" } => $"Testです",
        { Property3: true } => $"Trueです",
        _ => throw new ArgumentException(value.ToString()),
    };
    Debug.WriteLine(result);
}
Sample(new SampleClass1() { Property1 = 1 });
// 1です

Sample(new SampleClass1() { Property2 = "Test" });
// Testです

Sample(new SampleClass1() { Property3 = true });
// Trueです

Sample(new SampleClass1() { Property1 = 1, Property2 = "Test", Property3 = true });
// (^^)/

プロパティ判定+比較演算(when)

かなり自由に書ける。

public void Sample(SampleClass1 value) 
{
    var result = value switch
    {
        { Property1: var x } when x > 0  => $"{x}:0より大きいです",
        { Property2: var x } when (x?.Length ?? 0) > 3  => $"{x}:3文字以上です",
        { Property3: var x } when x => $"{x} です",
        _ => throw new ArgumentException(value.ToString()),
    };
    Debug.WriteLine(result);
}
Sample(new SampleClass1() { Property1 = 1 });
// 1:0より大きいです

Sample(new SampleClass1() { Property2 = "Test" });
// Test:3文字以上です

Sample(new SampleClass1() { Property3 = true });
// True です

タイプ判定

  • switch 文でも判定できたが、switch 式でより簡潔に判定できる。

以下のクラスを使用する。

public class SampleClass1
{
    public int Property1 { get; set; }
}
public class SampleClass2
{
    public string Property2 { get; set; }
}
public class SampleClass3
{
    public bool Property3 { get; set; }
}
public void Sample<T>(T value)  where T : class
{
    var result = value switch
    {
        SampleClass1 x => $"{x.GetType().Name}:{x.Property1}",
        SampleClass2 x => $"{x.GetType().Name}:{x.Property2}",
        SampleClass3 x => $"{x.GetType().Name}:{x.Property3}",
        _ => throw new ArgumentException(typeof(T).Name),
    };
    Debug.WriteLine(result);
}
Sample(new SampleClass1() { Property1 = 10 });
// SampleClass1:10

Sample(new SampleClass2() { Property2 = "Test" });
// SampleClass2:Test

Sample(new SampleClass3() { Property3 = true });
// SampleClass3:True



以上

C# - 最上位レベルステートメント

最上位レベルステートメント

docs.microsoft.com

  • C# 9.0 以降で使用できる。
  • main メソッドから外側の記述を省略できる。
  • 以下のフレームワークで、最上位レベルステートメントであることを確認。
  • main メソッド内の記述のため...
    • メソッドは定義できる。ローカル関数扱い。
    • プロパティ、読取専用フィールドは定義できない。

この記述が...

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

これだけでOK。

using System;

Console.WriteLine("Hello World!");

args 引数

  • 省略されているが使用できる。
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

var value = args[0];

await キーワード

  • 非同期メソッドを使用できる。
  • async は記述不要。
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

await Task.Delay(1000);

フレームワークごとのエントリーポイント

コンソール

using System;

Console.WriteLine("Hello World!");

ASP.NET Core MVC

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

WPF

/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}

Form

internal static class Program
{
    /// <summary>
    ///  The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        // To customize application configuration such as set high DPI settings or default font,
        // see https://aka.ms/applicationconfiguration.
        ApplicationConfiguration.Initialize();
        Application.Run(new Form1());
    }
}



以上

Git - コミットメッセージ

メッセージの書き方

例1

Gitコミットメッセージの7大原則 - rochefort's blog

How to Write a Git Commit Message

The seven rules of a great Git commit message

1. Separate subject from body with a blank line
2. Limit the subject line to 50 characters
3. Capitalize the subject line
4. Do not end the subject line with a period
5. Use the imperative mood in the subject line
6. Wrap the body at 72 characters
7. Use the body to explain what and why vs. how
No. 内容 補足
1 subject と body は空行で区切る
2 subject は50文字までにする オーバーする場合、変更過多の可能性あり
3 subject は大文字で始める 日本語なら、該当しない
4 subject は句点(。)で終わらない 無駄スペースのため
5 subject は命令形にする 敬語で丁寧に記載する必要はない
6 body は72文字までにする
7 body は how でなく what・why を記述する 理由が大事

例2

Gitのコミットメッセージの書き方 - Qiita

  • 以下の構成にする。
    • 1行目:変更内容の要約(タイトル、概要)
    • 2行目:空行
    • 3行目以降:変更した理由(内容、詳細)
  • 1行目に、コミット種別と要約を書く。
    • コミット種別
      • fix:バグ修正
      • hotfix:クリティカルなバグ修正
      • add:新規(ファイル)機能追加
      • などなど
  • 3行目に、変更理由を具体的に書く。
    • Redmine等のチケット番号 なども。


補足

GitHub

  • 51文字以上の場合、非推奨のメッセージが表示される。
  • 詳細はいくらでも記述できそう。10,000文字は書けることを確認。

Visual Studio

  • GitHub での見え方を試す。
  • 1行目は、GitHub で件名として表示される。
  • 2行目以降は、GitHub で詳細情報として表示される。
  • 1行目と2行目の間に空行を複数挿入しても、GitHub 上で補正される。



以上

Git for Windows

セットアップ

インストール

  • VSCode を事前にインストールしておく。
  • Git for Windowsをダウンロード。

  • デフォルトで進む。

  • デフォルトのエディターに "VSCode" を選択。

  • デフォルトで進む。

  • "Checkout as-is, commit as-is" を選択。
    ※ これ以外の場合、改行コードが自動変換される。

  • デフォルトで進む。

  • インストールの開始。
    完了後、git コマンドが使える。

ユーザー設定

  • "git config" コマンドでユーザー情報を登録。
  • コミットログなどでユーザーを特定するために必要。
    • name はダブルクォーテーションあり
    • email はダブルクォーテーションなし
git config --global user.name "GitHub に登録したユーザー名"
git config --global user.email GitHub に登録したメールアドレス

以下で config の内容を一覧で確認できる。

git config -l

プロキシがある場合、追加で以下も必要。(内容は環境に合わせる)
ダブルクォーテーションは不要

git config --global http.proxy http://proxy.example.com:8080
git config --global https.proxy http://proxy.example.com:8080

SSH 接続

  • SSH 接続のため、GitHub に公開鍵を登録する。
  • ssh-keygen コマンドで公開鍵・秘密鍵を作成する。
  • 前述のインストール後、コマンドを使用できる。
    • t:暗号化方式
    • b:暗号化強度
    • c:コメント(GitHubのメールアドレスを使用)
  • パスフレーズは適当に "1234" で試す。
ssh-keygen -t rsa -b 4096 -C "GitHub に登録したメールアドレス"
PS C:\GitSample> ssh-keygen -t rsa -b 4096 -C "メールアドレス"
Generating public/private rsa key pair.
Enter file in which to save the key (C:\Users\ユーザー名/.ssh/id_rsa):
Created directory 'C:\Users\ユーザー名/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in C:\Users\ユーザー名/.ssh/id_rsa.
Your public key has been saved in C:\Users\ユーザー名/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:Tnm+IzGQi1Bd/QCiwgi6yZmI7pQ5Pd7hRXSphQAu99w メールアドレス
The key's randomart image is:
+---[RSA 3072]----+
|.  ..o..oo       |
|oo. ..o.. +      |
|o.o+.  + + o     |
|+o*.o = +.  .    |
|=+ . + ES .      |
|. + . oooo       |
| * o . ..o.      |
|o o + o . ..     |
| . . o   ...     |
+----[SHA256]-----+
  • 秘密鍵と公開鍵が生成される。(ユーザーフォルダーに .ssh が生成される)

  • "id_rsa.pub" (公開鍵)の内容を GitHub に登録する。

  • 以下で接続に成功すれば完了。

ssh -T git@github.com

コマンド

クローン

コマンド 内容
git clone https://github.com/***/PrivateSample.git HTTPS でクローン
git clone git@github.com:***/PrivateSample.git SSH でクローン

"git clone" の後に、GitHub のコピー内容をペーストする。

ブランチ

コマンド 概要 内容
git branch 表示 ローカルブランチの一覧表示
作業ブランチには "*" が付く
git branch -r 表示 リモートブランチの一覧表示
git branch -a 表示 ローカルとリモートブランチの一覧表示
git branch ブランチ名 作成 現在のブランチから派生ブランチを作成
git checkout -b ブランチ名 origin/ブランチ名 作成 リモートブランチからローカルブランチを作成
git checkout ブランチ名 変更 指定のブランチにチェックアウト
git branch -m ブランチ名 変更 現在のブランチの名称を変更
git branch -d ブランチ名 削除 指定のブランチを削除

コミット

コマンド 内容
git status 状態の確認
git add ファイルパス 指定ファイルをステージに追加
git add . 変更をすべてステージに追加
git restore --staged ファイルパス 指定ファイルをステージから除外
git restore --staged . ステージからすべてを除外
git commit -m 'メッセージ' ステージの内容でコミット

コミットログ

コマンド 内容
git log コミットログの表示
git log -n 3 3件のログを表示
git log --oneline --graph コミットログのグラフ表示

プル/プッシュ

コマンド 内容
git pull リモートリポジトリの変更を反映する
git fetch リモートリポジトリの変更をローカルリモートリポジトリに取り込む
git merge origin/ブランチ名 リモートリポジトリのブランチをローカルにマージする
git push リモートリポジトリにプッシュする



以上

Git - .git フォルダー

HEAD ファイル

  • 現在作業中のブランチ名が登録される。
ref: refs/heads/main

この内容を変更すると、作業のブランチ名が変化した。
未登録の名称にすると、コミットログが参照できなくなる。

index ファイル

  • インデックス(ステージング領域)の内容が登録される。
  • インデックスの内容をもとにコミットを行う。
  • 中身はバイナリデータ。



以上

Git - フェッチ/プル

ブランチ名 内容
リモートブランチ GitHub などのリモートリポジトリにあるブランチ
リモート追跡ブランチ "origin/ブランチ名" ブランチ
リモートブランチのコピー。リモートブランチの変更を追跡する。
作業ブランチ ローカルでコミットするブランチ

フェッチ (fetch)

  • "リモートブランチ" の変更を "リモート追跡ブランチ" に反映する。
  • 作業ブランチには影響しない。
  • ブランチやコミットログなど、リモート側の情報を把握できる。

プル (pull)

  • フェッチの操作をした後...
    "リモート追跡ブランチ" の変更を "作業ブランチ" に反映する。
  • つまり、プルは "フェッチ" と "マージ" の処理を行う。

git コマンドで試してみる。
sample リモートブランチを一部変更した状態で、ローカルリポジトリを操作する。


フェッチせず、マージすると "すでに最新" となる。

> git merge origin/sample
Already up to date.

フェッチして、リモート追跡ブランチに取り込む。
フェッチ後、ファイルの内容が変わっていないことを確認。

> git fetch
Enter passphrase for key '/c/Users/***/.ssh/id_rsa':
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (4/4), done.
Unpacking objects: 100% (4/4), 723 bytes | 103.00 KiB/s, done.
remote: Total 4 (delta 3), reused 0 (delta 0), pack-reused 0
From github.com:***/PrivateSample
   c71e6e5..3292d27  sample    -> origin/sample

リモート追跡ブランチをマージする。

> git merge origin/sample
Updating c71e6e5..3292d27
Fast-forward
 WpfApp1/App.xaml.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

この操作をプルなら1コマンドで行える。

> git pull
Enter passphrase for key '/c/Users/***/.ssh/id_rsa':
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (4/4), done.
Unpacking objects: 100% (4/4), 724 bytes | 90.00 KiB/s, done.
remote: Total 4 (delta 3), reused 0 (delta 0), pack-reused 0
From github.com::***/PrivateSample
   3292d27..ec4b5c4  sample    -> origin/sample
Updating 3292d27..ec4b5c4
Fast-forward
 WpfApp1/App.xaml.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

補足

origin

  • origin は、リモートリポジトリの別名。
  • origin がデフォルトの名称。リポジトリ新規作成時に設定される。

(.git フォルダー、 config ファイル)

[core]
    repositoryformatversion = 0
    filemode = false
    bare = false
    logallrefupdates = true
    symlinks = false
    ignorecase = true
[remote "origin"]
    url = https://github.com/***/PrivateSample.git
    fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
    remote = origin
    merge = refs/heads/main



以上

GitHub メモ

リポジトリ

リポジトリの作成

  • 左上のアイコンからホーム画面に移動。
  • 左側が初回、右側が2回目以降の作成画面。

  • リポジトリ名や各種設定を行い "Create repository" をクリック。

    • "Private" を選択すれば、非公開のリポジトリにできる。

Public/Private の変更

  • 該当リポジトリの "Settings" を選択。

  • "Danger Zone" にある "Change repository visibility" を選択。

  • "Make ***" を選択し、リポジトリ名を入力。

リポジトリの破棄


ブランチ

ブランチの作成

  • "Code" より、起点のブランチを選択し、ブランチ名を入力。
  • "Create branch: 新ブランチ名 form 'ベースブランチ名'" を選択。

ブランチの破棄

  • "Code" より、"branches" を選択。
  • 一覧から該当ブランチのゴミ箱を選択。
    Youru branches/Active branches のどちらでもOK。

デフォルトブランチの変更

  • "Settings" の "Branches" より変更できる。


コミットログ

一覧の確認

  • "Code" より、"*commits" を選択すれば、一覧を確認できる。

コミットの差分を確認

  • 一覧のハッシュ値を選択すれば、該当コミットの差分を確認できる。
  • "Split" or "Unified" で閲覧方法を変更できる。

コミット時点の内容を確認

  • 一覧の "<>" を選択すれば、その時点の内容を確認できる。
  • ダウンロードなどもできる。

.gitignore ファイル

作成方法

  • リポジトリ新規作成時の場合、"Add .gitignore" から追加。

  • 途中でファイルを追加する場合、".gitignore" と入力する。テンプレートを選択できる。

.gitignore で無視されない場合

  • Git のキャッシュが原因。
  • ローカルリポジトリ削除後、一から作ると上手くできた。
  • コマンド使えるなら、"git rm -r --cached" でキャッシュを消せる。

フォーク

フォークの実行

フォーク元の差分情報の取得

  • "Code" より、"Fetch Upstream" を選択。
  • "Fetch and merge" で取得できる。

プルリクエス

  • 自分の変更内容をリポジトリに反映するための依頼機能。
  • 別名:プルリク、PR
  • 注意:発行済みのプルリクは削除できない。
    • なので、プルリクに機密情報などを誤って記載しないこと。
    • Close 機能はあるが、それでは削除はできない。
    • 完全に消す場合、リモートリポジトリを消すしかなさそう。

プルリクエストの実行

  • "Pull requests" より、"New pull request" を選択。

  • リクエスト元 (base)、リクエスト先 (compare) のブランチを選択し、"Create pull request" を選択。

  • リクエスト内容を記入し、"Create pull request" を選択。

  • レビュー後、"Merge pull request" を選択。

  • "Confirm merge" を選択。

  • 結果が履歴に記録される。

プルリクのコンフリクト

  • ブランチを選択すれば、コンフリクトを把握できる。
    • この状態でも "Create pull request" を選択できる。
    • 事前にマージ先のブランチ情報を取得した方が良い。
  • "Create pull request" 選択後、"Resoleve conflicts" よりコンフリクトを修正できる。

  • 修正後 "Commit merge" を選択すれば、"Merge pull request" を選択できるようになる。

  • プルリク中に、リクエスト先のブランチが更新されると、自動でチェックされる。

    • コンフリクトを検知すると、"Resoleve conflicts" よりコンフリクトを修正する。
    • マージの際、リクエスト先ブランチの内容を優先した場合、以下のチェック処理で再度コンフリクトが発生することはない。



以上

Git - Visual Studio

クローン

  • "Git→リポジトリのクローン" を選択。

  • "GitHub" を選択。

  • "サインイン→GitHub" を選択。

  • GitHub の認証を行う。

  • クローンするリモートリポジトリを選択。

  • "***.sln" をダブルクリック。


ブランチ

ブランチの作成

  • "Git→新しいブランチ..." を選択。

  • リモートリポジトリを選択した場合、ブランチ名は自動で入力される。

ブランチの切替

  • 右下のブランチ名から、該当のブランチを選択。

プル

失敗する場合

Hint: You have divergent branches and need to specify how to reconcile them.
Hint: You can do so by running one of the following commands sometime before
Hint: your next pull:
Hint: 
Hint:   git config pull.rebase false  # merge
Hint:   git config pull.rebase true   # rebase
Hint:   git config pull.ff only       # fast-forward only
Hint: 
Hint: You can replace "git config" with "git config --global" to set a default
Hint: preference for all repositories. You can also pass --rebase, --no-rebase,
Hint: or --ff-only on the command line to override the configured default per
Hint: invocation.
Git failed with a fatal error.
Git failed with a fatal error.
Need to specify how to reconcile divergent branches.

Visual Studio での Git 設定 | Microsoft Docs

  • Git の設定が未設定の場合、エラーとなる。
  • "git config pull.rebase false" が既定の設定。
  • オプションの "ソース管理→Gitグローバル設定"を行う。

コミット

コミットの実行

  • "Git→コミットまたは一時退避" を選択。
  • "Git変更" より、変更するファイルを右クリックし、"ステージ" を選択。
  • メッセージを入力し、"ステージング済みをコミット" を選択。

コミットログの確認

  • "Git→ブランチ履歴の表示" を選択。
  • "ローカル履歴" に履歴が表示される。

  • ログを右クリックし、"コミットの詳細を表示" で変更内容を表示。

コミット名の変更

  • コミットの詳細より、"編集"を選択し、名称を変更。

コミットを元に戻す(Revert)

  • コミットの詳細より、"元に戻す"を選択。
    • "Revert" のメッセージが付与されたコミットが追加された。
    • 該当コミットについて、元に戻す変更が行われる。
    • なので、コミットが削除されるわけではない。

コミットのリセット(Reset)

  • コミットの詳細より、"リセット"を選択。

    • 該当コミットよりも子のコミットがすべて消える。
    • mixed の場合、ファイルが残る。
    • hard の場合、ファイルが削除される。

  • コミットをプッシュ済みでない場合、上記処理でリセットできる。

  • コミットをプッシュ済みの場合、コミットログの書き換えが必要。※ 非推奨
  • f オプションを付与してプッシュする。Git コマンドで行う。
git push origin ブランチ名 -f

マージ

マージの実行

  • "develop" と "develop2" を用意。

  • マージされるブランチをチェックアウトし、"Git→ブランチの管理" を選択。

  • "develop2 を develop にマージする" を選択。

  • develop にマージ完了。

コンフリクト

  • "develop" と "develop2" の同一行を修正。

  • 前述と同様、"develop2 を develop にマージする" を選択。

  • 競合が発生するので、右側の該当ファイルをダブルクリック。
  • 差異のある箇所にチェックボックスがあり、チェックありの内容でマージする。
  • "マージの許可"を選択すると、マージを確定させる。
    ( = ソース上のマージ用のコメントが消える)

  • コミットする。(競合発生時は明示的に操作が必要)

チェリーピック

  • cherry pick:自分にとって都合のいいものを選ぶ
  • 現在作業中の HEAD に特定のコミットを追加できる。

チェリーピックの実行

  • develop と develop2 を編集。

  • 作業ブランチを develop にし、develop2 の特定のコミットをチェリーピック。

  • develop に指定したコミットのみが追加される。

チェリーピックのコンフリクト

  • チェリーピック先に元データがない状態で、いきなり全てを反映できない。
  • なので、古いコミットから順々にチェリーピックする。
  • コンフリクト時のマージ方法は前述の通り。

スカッシュ

  • Squash:押しつぶす
  • 複数のコミット履歴を1つのコミット履歴にまとめる。
  • 履歴が把握しやすくなる。
  • 現在作業中の HEAD から複数のコミットを選択できる。

スカッシュの実行

  • develop ブランチに3件のコミットを追加。

  • develop ブランチをチェックアウトした状態で、
    HEADから3件のコミットを選択し、"コミットのスカッシュ" を選択。

リベース

  • マージした場合、マージ元のコミットログが消える。
    (※ fast forward だと消えない)
  • リベースすれば、コミットログを保持できる。
    (新たなコミットとして追加される)

リベースの実行

  • "develop" の後に、"develop2" のコミットを追加する。
  • "develop2" をチェックアウトして操作する。

  • 前述のリベース後、"develop2" を "develop" にマージすれば、コミットログが残る。

リベースのコンフリクト

  • コンフリクト時...
    • 相手側の修正を反映すると、自分のコミットは追加されない。
    • 自分の修正を反映すると、コミットが追加される。



以上