LINQ query does not compile when using double, float, or decimal instead of int

Viewed 495

Consider the following simplified example, which is modeled after code that searches through a list of objects and sorts them by assigning a score to each in an attempt to calculate relevancy (the details of the scoring in the real code aren't relevant here):

public class MyClass
{
    public string UserName { get; set; }
    public string UserDescription { get; set; }
}

public static void QueryTest()
{
    var somequerytext = "test";
    var accounts = new List<MyClass>();
    var resultsViaInt = accounts.Where( ( MyClass acct, int score ) => 
            MatchInt( acct.UserName, somequerytext, ref score, 2 ) ||
            MatchInt( acct.UserDescription, somequerytext, ref score, 1 ) 
        )
        .Select( ( acct, score ) => new { result = acct, score = score } )
        .OrderBy( x => x.score )
        .Select( x => x.result )
        .ToList();

    var resultsViaDouble = accounts.Where( ( MyClass acct, double score ) => 
            MatchDouble( acct.UserName, somequerytext, ref score, 2.0 ) ||
            MatchDouble( acct.UserDescription, somequerytext, ref score, 1.0 )
        )
        .Select( ( MyClass acct, double score ) => new { result = acct, score = score } )
        .OrderBy( x => x.score )
        .Select( x => x.result )
        .ToList();
}


public static bool MatchInt( string haystack, string needle, ref int score, int weight )
{
    if ( haystack.Contains( needle ) )
    {
        score = score + weight;
        return true;
    }
    return false;
}

public static bool MatchDouble( string haystack, string needle, ref double score, double weight )
{
    if ( haystack.Contains( needle ) )
    {
        score = score + weight;
        return true;
    }
    return false;
}

For some reason, the compiler throws the following errors for the second query in QueryTest():

For the "accounts" variable:

'List<MyClass>' does not contain a definition for 'Where' and the best extension method overload 'Queryable.Where<MyClass>(IQueryable<MyClass>, Expression<Func<MyClass, bool>>)' requires a receiver of type 'IQueryable<MyClass>'

For the lambda expression inside the query itself:

Delegate 'Expression<Func<MyClass, bool>>' does not take 2 arguments

I get identical errors if I try to use double, float, or decimal. Using int, however, works. I can make the rest of my work with using slightly larger integers instead of doubles, but I would like to get some idea of why the code above won't compile.

The second error seems odd, since the equivalent part of the first query is detected (correctly) as <Func<MyClass, int, bool>> whereas the query with the error is missing any reference at all to a parameter in the middle.

Why would it only work for int?

2 Answers

I think this is pretty close to what you were looking for.

        var resultsViaDouble = accounts
            .Select(acct =>
            {
                double score = 0;
                return new
                {
                    match = MatchDouble(acct.UserName, somequerytext, ref score, 2.0) || MatchDouble(acct.UserDescription, somequerytext, ref score, 1.0),
                    score = score,
                    acct = acct
                };
            })
            .Where(x => x.match)
            .OrderBy(x => x.score)
            .Select(x => x.acct)
            .ToList();

As to what was wrong with the approach in the OP, I think the comments have explained it thoroughly enough.

the overload of Where method which works is :

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, int, bool> predicate
)

(List<MyClass> is an IEnumerable)

and int parameter stands for index of element in a sequense

there is no such overload with double parameter

so you are using int score incorrectly here


I suggest to transform you code like this:

public static double MatchDouble(string haystack, string needle, double weight )
{
    return haystack.Contains( needle ) ? weight : 0;
}
var resultsViaInt = accounts
    .OrderBy( 
              acct => MatchDouble( acct.UserName, somequerytext, 2.0 ) +
                      MatchDouble( acct.UserDescription, somequerytext, 1.0 )  
            )
    .ToList();
Related