避免在通用黑板的拳击

时间:2014-06-17 08:38:53

标签: c# generics

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

存储黑板的系统不知道其中包含什么类型的对象,以及何时。特定密钥的编写者和读者总是知道并同意键值对的类型。为了便于实现,牺牲了编译时检查 - 有许多编写器和读者,并且它们不断被迭代。

我的黑板界面如下所示:

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

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

我的初始实现使用了Dictionary<object,object>,一切都很好。但是,这块黑板必须快速且无需分配。这是不容谈判的。如果一个编写器将一个浮动值推入黑板,那么天真的实现就会将int放入字典中。

我不能在实现类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.
    }
    // ...
}

我在这里缺少任何语法技巧,包括更改黑板界面,以避免拳击?任何反射黑客都会违反“快速”要求,即使您可以在没有分配的情况下实施它。

干杯。

2 个答案:

答案 0 :(得分:3)

虽然我不想想要这样做(而且我需要说服拳击的成本真的很重要),你可以拥有多个商店并拥有一个变量在你的方法中键入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 :(得分: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);
}

更多信息:Why is TypedReference behind the scenes? It's so fast and safe… almost magical!