从字典中获取原始词典.AsQueryable()

本文关键字:AsQueryable 原始 字典 获取 | 更新日期: 2023-09-27 18:37:08

>我有一个通用字典,它传递给一个只接受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作为数据源。在实现时返回一个字典。在我的代码中的不同位置,我有处理数据源的类。

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

从字典中获取原始词典.AsQueryable()

如果EnumerableQuery<T>包装类在尚未IQueryable<T>的东西上调用,则 .AsQueryable() 扩展方法返回该类的实例。

此包装类具有具有internal访问权限的 .Enumerable 属性,该属性提供对调用.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<T>的包装器,又可以提供对原始字典的访问。但另一方面,任何试图检索字典的人都必须知道泛型类型参数是什么(例如 <string, string><int, string>等)为了成功铸造它。

这里的主要问题是IQueryable将自己包裹在字典周围,而不是像IEnumerable那样<>而不是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>>进行实际转换而不这样做,我认为您必须承受打击并从中创建一本新词典。