見出し画像

【C#】学習備忘録 2020年7月編

unsafe

unsafe とは、C# でポインタを使いたい時に使用するキーワードです。
unsafe で記述されたブロックでのみポインタは使用可能となります。

using System;

class Program
{
   public static void Main()
   {
       unsafe
       {
           int* p; // ポインタの宣言
           int n = 5;

           p = &n; // p に n のアドレスを代入
           Console.WriteLine((int)p);
       }
   }
}

また unsafe は、クラスやメソッドにも使用可能です。

unsafe void UnsafeTest()
{
    char* p;
}

参考:
unsafe (C# リファレンス)
C#でポインタは使えるの?unsafeの使い方と基礎知識まとめ

Span<T>

Span<T>構造体(System名前空間)は、span (区間、範囲)という名前通り、連続してデータが並んでいるもの(配列など)の一定範囲を読み書きするために使う型です。

引用:Span構造体

Span<T> はスタックに割り当てられる ref 構造体です。
オブジェクトの実体が必ずスタックに置かれることが保証されている型となります。

読み取り専用の ReadOnlySpan<T> 構造体も存在します。

// 長さ 8 で配列作成
// C# の仕様で、全要素 0 で作られる
var array = new int[8];

// 配列の、2番目(0 始まりなので3要素目)から、3要素分の範囲
var span = new Span<int>(array, 2, 3);

// その範囲だけを 1 に上書き
for (int i = 0; i < span.Length; i++)
{
   span[i] = 1;
}

// ちゃんと、2, 3, 4 番目だけが 1 になってる
foreach (var x in array)
{
   Console.WriteLine(x); // 0, 0, 1, 1, 1, 0, 0, 0
}

Span<T> は、配列や文字列の一部分を直接参照しています。

例えば、string の Substring メソッドを使うと部分文字列をコピーした新しい別の string が生成されるため非効率です。 これに対して、Span<char>とSlice を使えば、コピーなしで部分文字列を参照できます

参考:Span構造体

ArraySegment<T>

基本的に Span と同じ事ができる認識で良い気がしました。
配列に限って言えば、「配列の一部分を指す型」として、昔からArraySegment<T>が存在しますが、以下の点などから、Span の方が優れています。

・Span<T>は、配列だけでなく文字列など色々なものを指せる
・Span<T> の方が効率的で、読み書きがだいぶ速い

IReadOnlyCollection<T>

コレクション内の要素が読み取り専用になり、 Add や Remove 、インデックスによる要素アクセスが不可になる。

IReadOnlyDictionary<TKey, TValue> や IReadOnlyList<T> も存在する。

【使い所】

引数にリストを渡す際に、IReadOnlyCollection を用いることで、関数内でそのリストが変更されないことが保証される。

List<Entity> RemoveUnnecessary(IReadOnlyCollection<Entity> entities)
{
   var list = new List<Entity>();
   // 省略
}

参考:
引数の型を何でも List にしちゃう奴にそろそろ一言いっておくか
【Unity】 IReadOnlyCollection<T>を使って読み取り専用のコレクションを作る

Enumerable.Repeat<TResult>

element の要素を count 分繰り返したシーケンスを生成します。

public static System.Collections.Generic.IEnumerable<TResult> Repeat<TResult> (TResult element, int count);

例えば以下のように使用します。

// 中身が { 1, 1, 1, 1, 1 } の int[5]
var tmpArray = Enumerable.Repeat(1, 5).ToArray();

// 中身が { 0, 0, 0, 0, 0 } の List<int>
var tmpList = Enumerable.Repeat(0, 5).ToList();

メモ:Enumerable.Repeat は値型専用のため、参照型に使ってはいけない

参考:
Enumerable.Repeat<TResult>(TResult, Int32) メソッド
Enumerable.Repeat()でハマった話

Lazy<T>

インスタンスの初期化処理を遅延させます。
初めて使用されるまでオブジェクトの作成が延期されます。

遅延初期化は主に、パフォーマンスの向上、無駄な計算の回避、およびプログラムのメモリ要件の削減を目的として使用されます。

Lazy<Orders> orders = new Lazy<Orders>(() => new Orders(100));

Console.WriteLine(orders.IsValueCreated); // まだ使用されてないので false

Console.WriteLine(orders.Value);

Console.WriteLine(lazy.IsValueCreated); // Valueにアクセスされたため true

遅延オブジェクトの作成後、遅延変数の Value プロパティが初めてアクセスされるまでは、Orders のインスタンスは作成されません。

参考:
限定的な初期化 : Microsoft Docs
C# × Lazy × どうやって使うのか調べてみた

Mathf.PingPong

public static float  PingPong(float t, float length)
 第一引数で指定した値 t を参照し、0から第二引数 length の間で値を返す。

例えば、Mathf.PingPong(2, 3) ならt=2は0~3の間に入っているので、2を返す。
Mathf.PingPong(4, 3)の場合、4は0~3の範囲を1だけオーバーしているためピンポンのように跳ね返り、3-1 = 2で2を返す。

ループした上下運動や左右運動によく使われる。

Enumerable.First

名前空間: System.Linq
シーケンスの最初の要素を返す。

このメソッドの使いどころは、引数に条件を指定することで最初に条件に合った要素を取得することです。

int[] numbers = new int[] { 1, 2, 3, 4, 5, 6 };

// 最初に見つかった偶数の値を取得
int result  = numbers.First( value => value % 2 == 0 );  // 2 が取得できる。


閲覧ありがとうございます。 コンテンツをいいねと思ってくださった方にサポートいただけると大変嬉しいです!