How to retrieve actual item from HashSet?

前端 未结 11 537
花落未央
花落未央 2020-12-02 17:55

I\'ve read this question about why it is not possible, but haven\'t found a solution to the problem.

I would like to retrieve an item from a .NET HashSet. I

11条回答
  •  萌比男神i
    2020-12-02 18:36

    Another Trick would do Reflection, by accessing the internal function InternalIndexOf of HashSet. Keep in mind the fieldnames are hardcoded, so if those change in upcoming .NET versions this will break.

    Note: If you use Mono, you should change field name from m_slots to _slots.

    internal static class HashSetExtensions
    {
        public delegate bool GetValue(HashSet source, T equalValue, out T actualValue);
    
        public static GetValue TryGetValue { get; }
    
        static HashSetExtensions() {
            var targetExp = Expression.Parameter(typeof(HashSet), "target");
            var itemExp   = Expression.Parameter(typeof(T), "item");
            var actualValueExp = Expression.Parameter(typeof(T).MakeByRefType(), "actualValueExp");
    
            var indexVar = Expression.Variable(typeof(int), "index");
            // ReSharper disable once AssignNullToNotNullAttribute
            var indexExp = Expression.Call(targetExp, typeof(HashSet).GetMethod("InternalIndexOf", BindingFlags.NonPublic | BindingFlags.Instance), itemExp);
    
            var truePart = Expression.Block(
                Expression.Assign(
                    actualValueExp, Expression.Field(
                        Expression.ArrayAccess(
                            // ReSharper disable once AssignNullToNotNullAttribute
                            Expression.Field(targetExp, typeof(HashSet).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(block, targetExp, itemExp, actualValueExp).Compile();
        }
    }
    
    public static class Extensions
    {
        public static bool TryGetValue2(this HashSet source, T equalValue,  out T actualValue) {
            if (source.Count > 0) {
                if (HashSetExtensions.TryGetValue(source, equalValue, out actualValue)) {
                    return true;
                }
            }
            actualValue = default;
            return false;
        }
    }
    

    Test:

    var x = new HashSet { 1, 2, 3 };
    if (x.TryGetValue2(1, out var value)) {
        Console.WriteLine(value);
    }
    

提交回复
热议问题