日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

.NET避免裝箱的方法_實用技巧

作者:老趙 ? 更新時間: 2022-04-25 編程語言

.NET提供struct類型,正確使用可以減少對象數量,從而降低GC壓力,提高性能。不過有時候我會發現,某些同學有這方面的意識,但是有時候一疏忽一偷懶,就沒有得到相應的效果了。這里舉一個真實的例子:假設我們要將一對int作為字典的鍵,用于映射到某些數據,那么你會怎么做?當然我們可以直接使用Tuple,但這樣就可能產生大量的對象。于是我們打算使用自定義的值類型:

private struct MyKey {
    private readonly int _a;
    private readonly int _b;

    public MyKey(int a, int b) {
        _a = a;
        _b = b;
    }
}

這么做正確嗎?假如你做一下測試,會發現它已經可以“正確使用”了,但實際上還是錯誤的。我們用它來做字典的鍵,會依賴GetHashCodeEquals兩個方法,由于MyKey沒有提供這兩個方法,就會自動使用System.ValueType里的實現,這便引起了裝箱。

好吧,那么我們就來實現一下:

private struct MyKey {
    // ...

    public override int GetHashCode() {
        // ...
    }

    public override bool Equals(object that) {
        // ...
    }
}

那么現在呢?可能現在您就會比較容易意識到,即便GetHashCode已經沒有問題了,但是Equals方法還是會引起裝箱,因為that參數依然是object類型。

怎么破?當然有辦法,因為像HashSet或是Dictionary集合其實都不會直接調用GetHashCodeEquals方法,都是通過一個IEqualityComparer對象來委托調用的:

public interface IEqualityComparer {
    bool Equals(T x, T y);
    int GetHashCode(T obj);
}

假如在創建集合的時候沒有提供比較器,則會使用默認的EqualityComparer.Default對象,它的構造方法是這樣的:

private static EqualityComparer CreateComparer() {
    Contract.Ensures(Contract.Result>() != null);

    RuntimeType t = (RuntimeType)typeof(T);
    // Specialize type byte for performance reasons 
    if (t == typeof(byte)) {
        return (EqualityComparer)(object)(new ByteEqualityComparer());
    }

    // If T implements IEquatable return a GenericEqualityComparer
    if (typeof(IEquatable).IsAssignableFrom(t)) {
        return (EqualityComparer)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter(
                (RuntimeType)typeof(GenericEqualityComparer), t);
    }

    // If T is a Nullable where U implements IEquatable return a NullableEqualityComparer
    if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) {
        RuntimeType u = (RuntimeType)t.GetGenericArguments()[0];
        if (typeof(IEquatable<>).MakeGenericType(u).IsAssignableFrom(u)) {
            return (EqualityComparer)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter(
                    (RuntimeType)typeof(NullableEqualityComparer), u);
        }
    }

    // If T is an int-based Enum, return an EnumEqualityComparer
    // See the METHOD__JIT_HELPERS__UNSAFE_ENUM_CAST and METHOD__JIT_HELPERS__UNSAFE_ENUM_CAST_LONG cases in getILIntrinsicImplementation 
    if (t.IsEnum && Enum.GetUnderlyingType(t) == typeof(int)) {
        return (EqualityComparer)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter(
                (RuntimeType)typeof(EnumEqualityComparer), t);
    }

    // Otherwise return an ObjectEqualityComparer 
    return new ObjectEqualityComparer();
}

可以看出,根據不同的情況它會使用各式不同的比較器。其中最適合我們的自然就是實現IEquatable接口的分支了。于是我們可以這么做:

struct MyKey : IEquatable {
    // ...

    public bool Equals(MyKey that) {
        // ...
    }
}

這才是最終符合我們要求的做法。

原文鏈接:http://blog.zhaojie.me/2013/04/dont-go-half-way-of-preventing-boxing.html