別のトリックはInternalIndexOf
、HashSetの内部関数にアクセスすることにより、Reflectionを実行します。フィールド名はハードコーディングされているため、今後の.NETバージョンで変更された場合、これは機能しなくなります。
注: Monoを使用する場合は、フィールド名をからm_slots
に変更する必要があります_slots
。
internal static class HashSetExtensions<T>
{
public delegate bool GetValue(HashSet<T> source, T equalValue, out T actualValue);
public static GetValue TryGetValue { get; }
static HashSetExtensions() {
var targetExp = Expression.Parameter(typeof(HashSet<T>), "target");
var itemExp = Expression.Parameter(typeof(T), "item");
var actualValueExp = Expression.Parameter(typeof(T).MakeByRefType(), "actualValueExp");
var indexVar = Expression.Variable(typeof(int), "index");
var indexExp = Expression.Call(targetExp, typeof(HashSet<T>).GetMethod("InternalIndexOf", BindingFlags.NonPublic | BindingFlags.Instance), itemExp);
var truePart = Expression.Block(
Expression.Assign(
actualValueExp, Expression.Field(
Expression.ArrayAccess(
Expression.Field(targetExp, typeof(HashSet<T>).GetField("m_slots", BindingFlags.NonPublic | BindingFlags.Instance)), indexVar),
"value")),
Expression.Constant(true));
var falsePart = Expression.Constant(false);
var block = Expression.Block(
new[] { indexVar },
Expression.Assign(indexVar, indexExp),
Expression.Condition(
Expression.GreaterThanOrEqual(indexVar, Expression.Constant(0)),
truePart,
falsePart));
TryGetValue = Expression.Lambda<GetValue>(block, targetExp, itemExp, actualValueExp).Compile();
}
}
public static class Extensions
{
public static bool TryGetValue2<T>(this HashSet<T> source, T equalValue, out T actualValue) {
if (source.Count > 0) {
if (HashSetExtensions<T>.TryGetValue(source, equalValue, out actualValue)) {
return true;
}
}
actualValue = default;
return false;
}
}
テスト:
var x = new HashSet<int> { 1, 2, 3 };
if (x.TryGetValue2(1, out var value)) {
Console.WriteLine(value);
}