Как позвонить в словарь.TryGetValue () где K: предикат <T>, V: перечисление - PullRequest
1 голос
/ 29 ноября 2010

У меня есть Dictionary<Predicate<double>, SomeEnum>:

var dic = new Dictionary<Predicate<double>, SomeEnum>
{
    { (d) => d < 10, SomeEnum.Foo },
    { (d) => d > 90, SomeEnum.Bar }
};

Я хочу позвонить TryGetValue(K, out V) против него так:

dic.TryGetValue(99)

и получить

SomeStruct.Bar

Но первым параметром для TryGetValue() является Predicate<T>, а не просто T.Как я могу делать то, что хочу?

Я нашел только грязный обходной путь:

var kpv = dic.FirstOrDefault(p => p.Key(99));
if (kpv.Key != null)
    var result = kpv.Value;

Есть ли другие способы?

Или как реализовать мою идеюправильно? - объявить ключ не как константу, а как сегмент.

Ответы [ 4 ]

3 голосов
/ 30 ноября 2010

Здесь есть пара неправильных вещей:

Predicate<double> не подходит для использования в качестве TKey.Ключ для словаря должен идентифицировать значение, а не вычислять значение.

Использование лямбд также не имеет никакого смысла.Поскольку они анонимны, вы не получите никакой эквивалентности и не сможете использовать словарь.

См. Этот пример кода для иллюстрации:

Predicate<double> fn_1 = d => d == 34.0d;
Predicate<double> fn_2 = d => d == 34.0d;

// Note: There are not equal
if (fn_1 == fn_2)
    Console.WriteLine("These are Equal?");

Во всяком случае, вы можете использовать список делегатов и выполнить каждый из них, чтобы найти подходящие, но на этом этапе вы должныожидать нескольких результатов.Если вы хотите получить только один результат, вы должны рассмотреть, какой порядок предикаты хранятся в вашем списке.

Не злоупотребляйте KeyValuePair как хак, потому что у вас нетTuple<T1,T2>.Было бы довольно легко создать класс, который имеет Predicate и SomeStruct.Посмотрите:

public class MySegment
{   
     public Predicate<double> Predicate {get;set;}
     public SomeStruct Result {get;set;}
}

Чтобы просмотреть последовательность предикатов и найти совпадающие, выглядело бы так:

...
List<MySegment> list = new List<MySegment>();
...
list.Add(new MySegment { Predicate = d => d < 10, Result = SomeStruct.Foo });
list.Add(new MySegment { Predicate = d => d > 90, Result = SomeStruct.Bar });

...

public IEnumerable<SomeStruct> GetResults(double input)
{ 
    foreach (var item in list)
        if (item.Predicate(input))
             yield return item.Result;
}
2 голосов
/ 29 ноября 2010

Этого нельзя сделать с помощью словаря, поскольку он использует хеш-значения, чтобы быстро определить, где искать конкретный ключ.

Как вы обнаружили, вы можете вызывать предикаты напрямую, но это потребует вызова O (n) функций, что не лучше, чем использование List, или даже большого оператора if / then / else.

Если ваша коллекция потенциальных предикатов слишком длинная, чтобы это можно было использовать, вам необходимо создать собственную структуру данных, соответствующую вашим целям. Если вы планируете определять значения только на основе целочисленных диапазонов, это не должно быть сложным, но оно может выйти из-под контроля, если ваши предикаты станут более сложными.

Кстати, язык F #, который имеет встроенную поддержку для такого определения, используя Выражения совпадения . Я не знаю, как обстоят дела с компиляцией веток, но я предполагаю, что это довольно умно.

Редактировать

Вот пример использования выражения соответствия в F # для чего-то вроде этого:

// Define the "choose" function
let choose value = 
    match value with
    | v when v < 10 -> 1
    | v when v > 90 -> 2
    | _ -> 0

// Test the "choose" function
let choice1 = choose 5
let choice2 = choose 15
let choice3 = choose 95

Приведенный выше код дает следующие значения:

choice1 = 1 
choice2 = 0 
choice3 = 2

Я никогда раньше не работал с F #, поэтому вам придется поискать, как использовать функцию из F # в программе на C #.

2 голосов
/ 29 ноября 2010

Если ваш список предикатов не слишком длинный, вы можете просто добавить их в List<KeyValuePair<Predicate<T>, V>> и затем выполнить запрос LINQ:

var lt10 = new KeyValuePair<Predicate<Double>, SomeStruct>(d => d < 10, SomeStruct.Foo);
var gt90 = new KeyValuePair<Predicate<Double>, SomeStruct>(d => d > 90, SomeStruct.Bar);
var predicates = new List<KeyValuePair<Predicate<Double>, SomeStruct>>() { lt10, gt90 };

var result = predicates.FirstOrDefault(p => p.Key(99));

Вам лучше использовать SomeStruct? вместоSomeStruct, кроме того, с тех пор FirstOrDefault даст однозначный результат, если он не совпадает ни с одним.

Если ваш список очень длинный, вы захотите рассмотреть некоторую структуру данных, которая разрешает запросыв диапазоне, как интервальное дерево .

0 голосов
/ 29 ноября 2010

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

...