【问题标题】:C#: Exposing type safe API over heterogeneous dictionaryC#:通过异构字典公开类型安全 API
【发布时间】:2021-08-27 07:15:04
【问题描述】:

我想通过特定的 JSON 格式公开一个类型安全的 API。到目前为止,这基本上是我所拥有的:

public class JsonDictionary
{
    Dictionary<string, Type> _keyTypes = new Dictionary<string, Type>  {
        { "FOO", typeof(int) },
        { "BAR", typeof(string) },
        };
    IDictionary<string, object> _data;
    public JsonDictionary()
    {
        _data = new Dictionary<string, object>();
    }
    public void Set<T>(string key, T obj)
    {
        if (typeof(T) != _keyTypes[key])
            throw new Exception($"Invalid type: {typeof(T)} vs {_keyTypes[key]}");
        _data[key] = obj;
    }

    public dynamic Get(string key)
    {
        var value = _data[key];
        if (value.GetType() != _keyTypes[key])
            throw new Exception($"Invalid type: {value.GetType()} vs {_keyTypes[key]}");
        return value;
    }
}

这可以很好地用作:

JsonDictionary d = new JsonDictionary();
d.Set("FOO", 42);
d.Set("BAR", "value");

但是读取值有点令人失望,并且依赖于后期绑定:

var i = d.Get("FOO");
var s = d.Get("BAR");
Assert.Equal(42, i);
Assert.Equal("value", s);

是否有一些 C# 魔法可以用来实现类型安全的泛型 Get&lt;T&gt; 而不是在这里依赖 dynamic(理想情况下应该在编译时检查类型)?我还想使用Set&lt;T&gt; 的模式,以便d.Set("BAR", 56); 触发编译警告。


如果需要,可以将Dictionary&lt;string, Type&gt; _keyTypes 设为static。以上只是进行中的工作。

【问题讨论】:

  • d.Get(new Random().NextDouble() &gt; 0.5 ? "FOO" : "BAR") 会返回什么类型?
  • 在这种情况下应该等于typeof(_keyTypes[key])
  • 但是编译器必须在编译时知道该表达式的类型(如果您想要编译时类型检查)。 typeof(_keyTypes[key]) 不是编译器可以知道的。检查必须在运行时完成,但不一定是dynamic。您是否考虑过与 Set 做类似的事情?
  • 使用 C++ 元编程,您可以从 enum 派生出 type,这在 C# 中不可能吗?
  • 没有。 C# 不是 C++。

标签: c# generics .net-core containers


【解决方案1】:

我正在使用类似的解决方案:

public class JsonDictionary
{
    public static readonly Key<int> Foo = new Key<int> { Name = "FOO" };
    public static readonly Key<string> Bar = new Key<string> { Name = "BAR" };
        
    IDictionary<string, object> _data;
    public JsonDictionary()
    {
        _data = new Dictionary<string, object>();
    }
    
    public void Set<T>(Key<T> key, T obj)
    {
        _data[key.Name] = obj;
    }

    public T Get<T>(Key<T> key)
    {
        return (T)_data[key.Name];
    }
    
    public sealed class Key<T>
    {
        public string Name { get; init; }
    }
}

【讨论】:

【解决方案2】:

我会将 T 转换为对象并使用 .GetType() 检查对象类型

public class JsonDictionary
    {
        Dictionary<string, Type> _keyTypes = new Dictionary<string, Type>  {
        { "FOO", typeof(int) },
        { "BAR", typeof(string) },
        };
        IDictionary<string, object> _data;
        public JsonDictionary()
        {
            _data = new Dictionary<string, object>();
        }
        public void Set(string key, object obj)
        {
            if (obj.GetType() != _keyTypes[key])
                throw new Exception($"Invalid type: {obj.GetType()} vs {_keyTypes[key]}");
            _data[key] = obj;
        }

        public object Get(string key)
        {
            return _data[key];
        }
    }

我这样试了,效果很好:

JsonDictionary d = new JsonDictionary();
d.Set("FOO", 42);
d.Set("BAR", "value");

var i = d.Get("FOO");
var s = d.Get("BAR");
Console.WriteLine(i);
Console.WriteLine(s);

但老实说,我不喜欢你想要达到的目标。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-02
    • 2016-12-30
    相关资源
    最近更新 更多