避免在普通黑板上装箱

本文关键字:黑板 | 更新日期: 2023-09-27 18:08:51

黑板是在运行时存储和获取通用键值对的对象。它可以通过Dictionary<object,object>实现。一些子系统写到黑板上,让其他子系统从上面读。

存储黑板的系统不知道黑板里是什么类型的对象,也不知道什么时候。特定键的写入器和读取器总是知道并同意键值对的类型。为了便于实现,编译时检查被牺牲了——有许多写入器和读取器,它们被不断地迭代。

我的黑板界面是这样的:

interface Blackboard {
    bool HasKey(object key);
    T GetValue<T>(object key);
}

作者创建并返回黑板,因此SetValue(key, value)可以是一个实现细节。

我最初的实现使用Dictionary<object,object>,一切都很好。然而,这个黑板必须是快速的和自由分配的。这是没有商量余地的。如果写入器将float值压入黑板,则朴素实现将int值框入字典。

我不能在实现类BlackboardImpl<ValueType> : Blackboard上使用泛型,因为值类型在黑板上不是恒定的。

我可以使用多个内部字典,Dictionary<object,float>, Dictionary<object,int>等后退Dictionary<object,object>,和许多SetValue函数,所以现在我不框插入。然而,由于GetValue函数来自接口,我不能对它施加约束,所以我仍然在exit上装箱:

T GetValue<T>(object key) {
    if (typeof(T) == typeof(int)) {
        // return intStore[key]; // doesn't compile
        return (T)(object)intStore[key]; // boxes, allocates, bad.
    }
    // ...
}

这里有什么语法技巧我错过了,包括改变黑板接口,以避免装箱?任何反射攻击都将违反"快速"要求,即使您可以在没有分配的情况下实现它。

欢呼。

避免在普通黑板上装箱

虽然我不希望这样做(而且我需要说服,装箱的成本真的会很重要),但您可以拥有多个存储,并在您的方法中使用Dictionary<object, T>类型的变量—这样我相信您可以避免装箱:

T GetValue<T>(object key)
{
    Dictionary<object, T> store;
    if (typeof(T) == typeof(int)
    {
         store = (Dictionary<object, T>) (object) intStore;
    }
    else if (typeof(T) == typeof(float))
    {
        store = (Dictionary<object, T>) (object) floatStore;
    }
    // etc - with a default or an error case for unhandled types.
    return store[key];
}
注意,这里的双强制转换是必要的,以使编译器满意,但它不涉及装箱

我不建议将其用于产品代码,但是使用未记录的__makeref__refvalue方法是将T视为int而不装箱的唯一方法

static T GetValue<T>()
{
    if (typeof(T) == typeof(int))
    {
        int i = intStore[key];
        T val = default(T);
        __refvalue(__makeref(val), int) = i;
        return val;
    }
    // ...
    return default(T);
}

更多内容:为什么TypedReference在幕后?它是如此的快速和安全……几乎是神奇的!