또 다른 트릭은 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);
}