Search code examples
c#linqdictionarypredicatetrygetvalue

How to call Dictionary<K, V>.TryGetValue() where K : Predicate<T>, V : enum


I have Dictionary<Predicate<double>, SomeEnum>:

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

I want to call TryGetValue(K, out V) against it like this:

dic.TryGetValue(99)

and receive

SomeStruct.Bar

But first param for TryGetValue() is Predicate<T>, not just T. How can I do what I want?

I found only a dirty workaround:

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

Are there other ways?

Or how to implement my idea properly? - declare a key not as a constant but like a segment.


Solution

  • There are a couple of things wrong here:

    Predicate<double> is not an appropriate type to use as a TKey. The key for a dictionary is supposed to identify a value, not calculate a value.

    This wouldn't make any sense using lambdas either. Because they are anonymous, you wouldn't get any equivalence, and won't be able use a dictionary.

    See this code sample for an illustration:

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

    If anything, you could use a list of delegates and execute each one to find the ones that match, but at that point you must expect multiple results. If you only want to get a single result, then you have to consider which order the predicates are stored within your list.

    Don't misuse KeyValuePair as a hack for not having Tuple<T1,T2>. It would be fairly easy to create a class that has both a Predicate and a SomeStruct. Look:

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

    To go through a sequence of predicates, and find the matching ones would look like this:

    ...
    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;
    }