再利用可能なジェネリックユーティリティ
小さく再利用可能なヘルパーを作ります。Guardチェック、キーに基づく等価比較子、純粋関数のメモ化、シンプルなResult を扱います。
「再利用可能なジェネリックユーティリティ」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン3/3です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全3レッスンが含まれています。
ツールキットの計画
目標: プロジェクト間で再利用できる小さなヘルパーを集めます。
- 引数のGuardチェック
- 等価性のためのキー基準の比較器
- 純粋関数のメモ化
- 成功/エラーのフローに使うResult<T>
ガードチェック
小さなGuardクラスに引数検証を集約し、メソッドの開始時に呼び出します。
using System;
public static class Guard
{
public static T NotNull<T>(T value, string name) where T : class
{
if (value == null) throw new ArgumentNullException(name);
return value;
}
public static string NotNullOrEmpty(string value, string name)
{
if (string.IsNullOrEmpty(value)) throw new ArgumentException("must not be null or empty", name);
return value;
}
}
public class Program
{
public static void Main(string[] args)
{
// OK
string name = Guard.NotNullOrEmpty("Ada", "name");
Console.WriteLine("Hello, " + name);
// Uncomment to see guard throw:
// Guard.NotNull<string>(null, "input");
}
}
キー基準の比較器
モデルのEquals/GetHashCodeに触れず、キー(例: Id)によって複雑なオブジェクトを比較します。
using System;
using System.Collections.Generic;
public sealed class KeyEqualityComparer<T, TKey> : IEqualityComparer<T>
{
private readonly Func<T, TKey> _keySelector;
private readonly IEqualityComparer<TKey> _keyComparer;
public KeyEqualityComparer(Func<T, TKey> keySelector)
: this(keySelector, EqualityComparer<TKey>.Default) {}
public KeyEqualityComparer(Func<T, TKey> keySelector, IEqualityComparer<TKey> keyComparer)
{
if (keySelector == null) throw new ArgumentNullException("keySelector");
if (keyComparer == null) throw new ArgumentNullException("keyComparer");
_keySelector = keySelector;
_keyComparer = keyComparer;
}
public bool Equals(T x, T y)
{
if (object.ReferenceEquals(x, y)) return true;
if ((object)x == null || (object)y == null) return false;
return _keyComparer.Equals(_keySelector(x), _keySelector(y));
}
public int GetHashCode(T obj)
{
if ((object)obj == null) return 0;
TKey key = _keySelector(obj);
return key == null ? 0 : _keyComparer.GetHashCode(key);
}
}
public sealed class User
{
public int Id;
public string Name;
}
public class Program
{
public static void Main(string[] args)
{
List<User> list = new List<User>();
list.Add(new User { Id = 1, Name = "Ada" });
list.Add(new User { Id = 1, Name = "Ada Lovelace" }); // same Id -> considered equal
list.Add(new User { Id = 2, Name = "Alan" });
// Deduplicate by Id using a HashSet with our comparer
HashSet<User> set = new HashSet<User>(new KeyEqualityComparer<User,int>(delegate(User u) { return u.Id; }));
foreach (User u in list) set.Add(u);
Console.WriteLine("Unique by Id = " + set.Count); // 2
}
}
メモ化ヘルパー
Memoizeは純粋関数の結果をキャッシュします。同じ入力で呼び出す処理を繰り返す場合に使います。
using System;
using System.Collections.Generic;
public static class Memo
{
// Wrap a pure function f: K -> V with a simple cache
public static Func<TKey, TValue> Memoize<TKey, TValue>(Func<TKey, TValue> f)
{
if (f == null) throw new ArgumentNullException("f");
Dictionary<TKey, TValue> cache = new Dictionary<TKey, TValue>();
return delegate(TKey key)
{
TValue v;
if (cache.TryGetValue(key, out v)) return v;
v = f(key);
cache[key] = v;
return v;
};
}
}
public class Program
{
static int SlowSquare(int x)
{
// Simulate work
System.Threading.Thread.Sleep(100);
return x * x;
}
public static void Main(string[] args)
{
Func<int,int> fast = Memo.Memoize<int,int>(SlowSquare);
var t1 = DateTime.UtcNow; Console.WriteLine(fast(12));
var t2 = DateTime.UtcNow; Console.WriteLine(fast(12)); // from cache
var dt1 = (t2 - t1).TotalMilliseconds;
var t3 = DateTime.UtcNow; Console.WriteLine(fast(12)); // from cache again
var t4 = DateTime.UtcNow; var dt2 = (t4 - t3).TotalMilliseconds;
Console.WriteLine("First vs cached ms: " + dt1 + " / " + dt2);
}
}
Result<T>ラッパー
最小限のResult<T>型により、成功とエラーを明示でき、表示やテストも簡単になります。
using System;
public enum ResultKind { Ok, Error }
public sealed class Result<T>
{
public ResultKind Kind;
public T Value; // valid when Ok
public string Error; // valid when Error
private Result() {}
public static Result<T> Ok(T value)
{
Result<T> r = new Result<T>();
r.Kind = ResultKind.Ok;
r.Value = value;
r.Error = null;
return r;
}
public static Result<T> Fail(string error)
{
if (string.IsNullOrEmpty(error)) throw new ArgumentException("error");
Result<T> r = new Result<T>();
r.Kind = ResultKind.Error;
r.Error = error;
r.Value = default(T);
return r;
}
public override string ToString()
{
return Kind == ResultKind.Ok ? "OK: " + (Value == null ? "null" : Value.ToString())
: "ERROR: " + Error;
}
}
public class Program
{
static Result<int> ParsePositiveInt(string s)
{
int n;
if (!Int32.TryParse(s, out n)) return Result<int>.Fail("not a number");
if (n <= 0) return Result<int>.Fail("must be > 0");
return Result<int>.Ok(n);
}
public static void Main(string[] args)
{
Console.WriteLine(ParsePositiveInt("12"));
Console.WriteLine(ParsePositiveInt("-1"));
Console.WriteLine(ParsePositiveInt("x"));
}
}
実践的なヒント
ヒント:
- ヘルパーは小さく、目的を絞ります。
- Guard、KeyEqualityComparer、Memoのように、明確な名前を付けます。
- 制約と使用すべき場面(Memoには純粋関数)をドキュメント化します。
- 各ヘルパーに簡単なユニットテストを1つ書きます。
キー比較器の利点
振り返り
振り返り: 小さなツールを再利用します。入力にはGuard、等価性にはKeyEqualityComparer、純粋関数にはMemo、結果の明示にはResult<T>を使います。
よくある質問
「再利用可能なジェネリックユーティリティ」レッスンは無料ですか?
はい。「再利用可能なジェネリックユーティリティ」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全3レッスンが含まれています。
「再利用可能なジェネリックユーティリティ」で何を学びますか?
小さく再利用可能なヘルパーを作ります。Guardチェック、キーに基づく等価比較子、純粋関数のメモ化、シンプルなResult を扱います。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C# Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/3です。
「再利用可能なジェネリックユーティリティ」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC# Academyレッスンでコードを書いて実行できますか?
はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- unmanaged、notnull、new()(C# 6でのエミュレーション)
- ジェネリック数学(概要)とジェネリック属性(関連する場合)
- 再利用可能なジェネリックユーティリティ