从 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>
等)才能成功转换它。
我有一个通用字典,它传递给一个只接受 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>
等)才能成功转换它。