2015-01-12 5 views
7

У меня есть общий словарь, перейти на метод, который принимает только IQueryable в качестве параметраОригинального словарь из dict.AsQueryable()

Можно ли бросить запрашиваемую обратно к первоначальному словарю? И я не имею в виду создание нового словаря с .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 в качестве источника данных. При реализации возвращает словарь. В другом месте моего кода у меня есть классы, которые обрабатывают dataSources.

Процессор знает, что dataSource будет Dictionary, но я не хочу иметь накладные расходы для создания другого Словаря, если у меня его уже есть.

ответ

6

Метод расширения .AsQueryable() возвращает экземпляр EnumerableQuery<T> wrapper class, если он вызван тем, что еще не было IQueryable<T>.

Этот класс обертки имеет свойство .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<T> и обеспечения доступа словаря к первоначальному словаря , Но, с другой стороны, любой, кто пытается получить словарь, должен был знать, что такое параметры типового типа (например, <string, string>, <int, string> и т. Д.), Чтобы успешно выполнить его.

+0

Подсказка с закрытым полем «Перечислимые».Я даже включил резерв в свой код ('if (pInfo == null), затем создайте новый словарь и добавьте KeyValuePairs вручную'), чтобы поддерживать совместимость с будущими версиями. –

2

Основная проблема заключается в том, что IQueryable обертывается вокруг Словаря, а не как IEnumerable <> над IDictionary <>, где вы можете его вернуть.

Вы можете, конечно, узнать, если тип завернутый представляет собой словарь, если вы знаете типы участвующий:

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

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

Если вы не возражаете, достигая в объектах внутренности, вы можете использовать частное Enumerable поле на EnumerableQuery получить версию (возможно) оригинальный словарь назад как IEnumerable<>

Но на самом деле преобразовать из EnumerableQuery<KeyValuePair<int,int>> прячась под IQueryable, не делая, что я думаю, вы должны просто принять удар и создать новый Dictio из него.

Смежные вопросы