overslacked
overslacked

Reputation: 4137

Lambdas over polymorphic classes

In the code below, note that Bar derives from Foo.

class Program
{
    public class Foo
    {
        public string data { get; set; }
    }

    public class Bar : Foo
    {
    }

    static void Main(string[] args)
    {
        var bars = new List<Bar>();

        bars.Add(new Bar() { data = "hello" });
        bars.Add(new Bar() { data = "world" });

        var resultsA = GetFoos(bars, (b => b.data.StartsWith("h")));
        var resultsB = GetBars(bars, (b => b.data.StartsWith("h")));
    }

    static List<Foo> GetFoos(List<Foo> fooList, Func<Foo, bool> criteria)
    {
        return fooList.Where(criteria).ToList();
    }

    static List<Bar> GetBars(List<Bar> barList, Func<Bar, bool> criteria)
    {
        return barList.Where(criteria).ToList();
    }
}

The GetFoos method call results in this compiler error message: Argument 1: cannot convert from 'System.Collections.Generic.List<Program.Bar>' to 'System.Collections.Generic.List<Program.Foo>'

However, when that line is commented out, the call to GetBars() executes correctly.

Is what I'm trying to accomplish here, a query against a common ancestor class, possible with linq?

Upvotes: 3

Views: 90

Answers (2)

RagtimeWilly
RagtimeWilly

Reputation: 5445

Update the List to IEnumerable:

static IEnumerable<Foo> GetFoos(IEnumerable<Foo> fooList, Func<Foo, bool> criteria)
{
    return fooList.Where(criteria).ToList();
}

static IEnumerable<Bar> GetBars(IEnumerable<Bar> barList, Func<Bar, bool> criteria)
{
    return barList.Where(criteria).ToList();
}

This is because IEnumerable<Bar> is a subtype of IEnumerable<Foo>. The subtyping is preserved because IEnumerable<T> is covariant on T.

Neither IList<Boo> nor IList<Foo> is a subtype of the other, because IList<T> is invariant on T.

Upvotes: 4

BradleyDotNET
BradleyDotNET

Reputation: 61369

Only interfaces can be covariant. Since you are trying to assign a List<Derived> to a List<Base>, you need to use a covariant interface instead. IEnumerable is already covariant, so just change your code to:

static List<Foo> GetFoos(IEnumerable<Foo> fooList, Func<Foo, bool> criteria)
{
    return fooList.Where(criteria).ToList();
}

static List<Bar> GetBars(IEnumerable<Bar> barList, Func<Bar, bool> criteria)
{
    return barList.Where(criteria).ToList();
}

Proof on IdeOne

Upvotes: 6

Related Questions