从 dict.AsQueryable() 获取原始字典

Get original dictionary from dict.AsQueryable()

我有一个通用字典,它传递给一个只接受 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.???
}

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

处理器知道数据源将是一个字典,但如果我已经有一个字典,我不想再创建另一个字典的开销。

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

如果你知道涉及的类型,你肯定能查出被包裹的类型是不是字典:

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

isDictionary<KeyValuePair<string,string>>(dataSource);

如果您不介意深入了解对象内部结构,您可以使用 EnumerableQuery 上的私有 Enumerable 字段来获取(可能)原始字典的一个版本作为 IEnumerable<>

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

.AsQueryable() 扩展方法 returns EnumerableQuery<T> wrapper class 的一个实例,如果它被调用的对象还不是 IQueryable<T>

此包装器 class 具有 .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() 的内部实现在将来的某个时候不会改变。因此,最好的办法是要么找到一种方法使原始词典易于访问,要么继续制作新词典。


一种可能的解决方法(不是很好)是制作自己的包装器 class 来携带字典:

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<T> 的包装器,又可以提供对原始字典的访问。但另一方面,任何试图检索字典的人都必须知道泛型类型参数是什么(例如 <string, string><int, string> 等)才能成功转换它。