避免将通用黑板限制为特定领域

5

黑板是一个在运行时存储和获取通用键值对的对象。它可以通过Dictionary<object,object>实现。一些子系统将信息写入黑板,以便其他子系统从中读取。

存储黑板的系统不知道其中的对象类型及其何时存在。每个针对特定键的写入者和读取者总是知道并同意键值对的类型。为了实现方便而牺牲了编译时检查 - 有大量的写入者和读取者,并且它们不断迭代。

我的黑板接口如下:

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

作家创建并返回黑板,因此SetValue(key, value)可以是实现细节。
我的初始实现使用了Dictionary<object,object>,一切都很好。然而,这个黑板必须快速且无需分配内存。这是不可协商的。如果作家将一个浮点值推入黑板中,那么天真的实现会将整数装箱放入字典中。
我不能在实现类上使用通用类型BlackboardImpl<ValueType> : Blackboard,因为值类型在黑板之间不是常量。
我可以使用多个内部字典,如Dictionary<object,float>Dictionary<object,int>等,带有后备Dictionary<object,object>和许多SetValue函数,因此现在我在插入时不再进行装箱。然而,由于GetValue函数来自接口,我无法对其进行约束,因此仍然在退出时进行装箱:
T GetValue<T>(object key) {
    if (typeof(T) == typeof(int)) {
        // return intStore[key]; // doesn't compile
        return (T)(object)intStore[key]; // boxes, allocates, bad.
    }
    // ...
}

有没有什么语法技巧可以避免装箱,包括更改黑板界面?任何反射黑科技都将违反“快速”要求,即使您可以在不分配内存的情况下实现它。

干杯。


@NicoSchertler: 不,使用“dynamic”肯定会装箱。 - Jon Skeet
@JonSkeet:好的,我不确定那个。已删除评论。 - Nico Schertler
你能尝试使用void*unsafe吗? - rhughes
2个回答

4

虽然我不想这样做(而且我需要被说服,证明使用装箱的成本确实很高),但是你可以拥有多个商店,并在你的方法中使用一个类型为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];
}

请注意,这里需要双重转换以使编译器满意,但这不会涉及装箱。

1

虽然不建议在生产代码中使用,但使用未记录的 __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在幕后?它如此快速和安全...几乎像是魔法!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接