从字典中获取原始词典.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
作为数据源。在实现时返回一个字典。在我的代码中的不同位置,我有处理数据源的类。
处理器知道数据源将是一个字典,但如果我已经有一个字典,我不想有创建另一个字典的开销。
如果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>>
进行实际转换而不这样做,我认为您必须承受打击并从中创建一本新词典。