【问题标题】:Get original dictionary from dict.AsQueryable()从 dict.AsQueryable() 获取原始字典
【发布时间】:2015-01-12 12:53:33
【问题描述】:

我有一个通用字典,它传递给一个只接受IQueryable 作为参数的方法

是否可以将可查询的内容转换回原始字典?我不是说用.ToDictionary(...)创建一个新字典

private static void Main()
{

    var dict = new Dictionary<int, int>();
    dict.Add(1,1);

    SomeMethod(dict.AsQueryable());

}

public static void SomeMethod(IQueryable dataSource)
{
    // dataSource as Dictionary<int, int> --> null
    var dict = dataSource.???
}

我知道在这个简单的例子中这没有多大意义。但总的来说,我有一个界面,要求我返回一个IQueryable 作为数据源。在实现时返回一个字典。在我的代码的不同位置,我有处理数据源的类。

处理器知道 dataSource 将是一个字典,但如果我已经有另一个字典,我不希望有创建另一个字典的开销。

【问题讨论】:

    标签: c# .net dictionary iqueryable


    【解决方案1】:

    .AsQueryable() 扩展方法返回一个 EnumerableQuery&lt;T&gt; wrapper class 的实例,如果它被调用的对象还不是 IQueryable&lt;T&gt;

    这个包装类有一个.Enumerable 属性和internal 访问,提供对调用.AsQueryable() 的原始对象的访问。所以你可以这样做来取回你原来的字典:

    var dict = new Dictionary<int, int>();
    dict.Add(1,1);
    var q = dict.AsQueryable();
    
    
    
    Type tInfo = q.GetType();
    PropertyInfo pInfo = tInfo.GetProperties(BindingFlags.NonPublic | 
                                             BindingFlags.Instance)
                              .FirstOrDefault(p => p.Name == "Enumerable");
    if (pInfo != null)
    {
        object originalDictionary = pInfo.GetValue(q, null);
    
        Console.WriteLine(dict == originalDictionary);  // true
    }
    

    但是,这通常是一个非常糟糕的主意。 internal 成员的访问受限是有原因的,我认为不能保证 .AsQueryable() 的内部实现在将来的某个时候不会改变。所以你最好的选择是要么找到一种方法让原始字典可以访问,要么继续制作一个新字典。


    一种可能的解决方法(不是很好)是制作自己的包装类来携带字典:
    private class DictionaryQueryHolder<TKey, TValue> : IQueryable<KeyValuePair<TKey, TValue>>
    {
        public IDictionary<TKey, TValue> Dictionary { get; private set; }
        private IQueryable<KeyValuePair<TKey, TValue>> Queryable { get; set; }
    
        internal DictionaryQueryHolder(IDictionary<TKey, TValue> dictionary)
        {
            Dictionary = dictionary;
            Queryable = dictionary.AsQueryable();
        }
    
        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return Queryable.GetEnumerator();
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    
        public Expression Expression
        {
            get { return Queryable.Expression; }
        }
    
        public Type ElementType
        {
            get { return Queryable.ElementType; }
        }
    
        public IQueryProvider Provider
        {
            get { return Queryable.Provider; }
        }
    }
    

    这既可以作为字典IQueryable&lt;T&gt; 的包装器,又可以提供对原始字典的访问。但另一方面,任何试图检索字典的人都必须知道泛型类型参数是什么(例如&lt;string, string&gt;&lt;int, string&gt; 等)才能成功转换。

    【讨论】:

    • 私有 Enumerable 字段的提示有效。我什至在我的代码 (if (pInfo == null) then create a new dictionary and add the KeyValuePairs manually) 中加入了后备,以保持与未来版本的兼容性。
    【解决方案2】:

    这里的主要问题是 IQueryable 将自己包裹在 Dictionary 周围,而不是像 IEnumerable 而不是 IDictionary 那样,您可以将其转换回去。

    如果您知道所涉及的类型,您当然可以找出包装的类型是否是字典:

    public bool isDictionary<T>(object obj) {
        return obj.GetType().GenericTypeArguments.Contains(typeof(T));
    }
    
    isDictionary<KeyValuePair<string,string>>(dataSource);
    

    如果您不介意进入对象内部,您可以使用 EnumerableQuery 上的私有 Enumerable 字段来获取(可能)原始字典的版本作为 IEnumerable&lt;&gt;

    但要真正从隐藏在IQueryable 下的EnumerableQuery&lt;KeyValuePair&lt;int,int&gt;&gt; 转换而不这样做,我认为您必须接受打击并从中创建一个新字典。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-14
      • 2016-06-11
      • 1970-01-01
      • 2023-04-10
      • 2020-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多