如何调用Dictionary <k,v =“”> .TryGetValue()其中K:Predicate <t>,V:enum </t> </k,>

时间:2010-11-29 20:01:14

标签: c# linq dictionary predicate trygetvalue

我有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 个答案:

答案 0 :(得分:3)

这里有一些问题:

Predicate<double>不适合用作TKey。字典的关键是识别值,而不是计算值。

使用lambdas也没有任何意义。因为它们是匿名的,所以你不会得到任何等价,也不会使用字典。

请参阅此代码示例以获取说明:

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?");

如果有的话,您可以使用委托列表并执行每个委托以查找匹配的委托,但此时您必须预期多个结果。如果您只想获得单个结果,那么您必须考虑谓词存储在列表中的 order

不要滥用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;
}

答案 1 :(得分:2)

如果您的谓词列表不是太长,您可以将它们添加到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与任何内容不匹配,{{1}}会给出明确的结果。

如果您的列表很长,您会考虑某种允许查询范围的数据结构,例如Interval Tree

答案 2 :(得分:2)

使用Dictionary无法完成此操作,因为它依赖于哈希值来快速确定查找特定键的位置。

正如您所发现的,您可以直接调用谓词,但这需要调用O(n)函数,这不比使用List,甚至是大的if / then / else语句更好。

如果您的潜在谓词集合太长而无法选择,那么您需要创建自己的数据结构以满足您的目的。如果您只打算根据整数范围定义值,这应该不难,但如果您的谓词变得更复杂,它可能会失控。

另一方面,F#语言使用Match Expressions内置支持此类定义。我不知道如何编译分支,但我认为它相当聪明。

修改

以下是在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#,所以你必须四处寻找如何在C#程序中使用F#中的函数。

答案 3 :(得分:0)

您必须遍历您的条件并针对输入运行每个谓词以查看它是否匹配。我认为没有理由在这里使用词典。