Reputation: 41
I have some part of code. It's not real code but very similar with my problem in production.
Call of ASupplier
in point 2 not compiled cause result of var filtered
is List<IA>
. It's look's like valid cause Where
declared as
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
But i can't understand why point 3 is valid because FixedIACondition declaration similar with result of call IAConditionCreator
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqWhereConditionProblem
{
internal class Program
{
public static void Main(string[] args)
{
var collection = new List<A>();
ASupplier(collection); // 1) OK
var filtered = collection.Where(IAConditionCreator("a")).ToList();
ASupplier(filtered); // 2) NOT OK
var filtered2 = collection.Where(FixedIACondition).ToList();
ASupplier(filtered2); // 3) OK
}
private static void ASupplier(IReadOnlyCollection<A> aCollection)
{
foreach (var a in aCollection)
{
Console.WriteLine(a.GetText());
}
}
private static Func<IA, bool> IAConditionCreator(string value)
{
return a => a.GetText() == value;
}
private static bool FixedIACondition(IA ia) => ia.GetText() == "aa";
}
public interface IA
{
string GetText();
}
public class A : IA
{
public string GetText()
{
return "ABC";
}
}
}
Upvotes: 4
Views: 81
Reputation: 8824
Changing method ASupplier
to receive IASupplier resolves this issue:
private static void ASupplier(IReadOnlyCollection<IA> aCollection)
{
foreach (var a in aCollection)
{
Console.WriteLine(a.GetText());
}
}
If you only care about the interface, this may be what you want. If ASupplier is solely to handle type A, you can either Cast or filter from the list those OfType A
ASupplier(filtered.OfType<A>().ToList()); // this will filter the list to return those of type A
ASupplier(filtered.Cast<A>().ToList()); // this will throw exception if any object can't be cast to A
Another option is casting the Func itself to return the type you want:
var condition = (Func<A, bool>)IAConditionCreator("a");
var filtered = collection.Where(condition).ToList();
ASupplier(filtered);
There are other ways to handle polymorphism, but then you'll have to be more specific about your scenario.
Upvotes: 0
Reputation: 5791
The way the compiler does implicit type conversion is different for delegate objects and method groups and I guess its main goal is to reduce the number of type conversions (including implicit type conversions) as far as possible.
The method FixedIACondition
is referenced as a method group that the compiler has to convert to a delegate object anyhow. The compile is able to infer that this method group can be converted into a Func<A, bool>
, which is the type required by .Where
.
For IAConditionCreator
, the compiler already has a delegate object and now tries to fit that object into the call. However, it has to convert either the collection to IEnumerable<IA>
or to convert the delegate to Func<A, bool>
. The support for covariant delegates is not that good in .NET (try calling Delegate.Combine
) and I guess the compiler team is aware of that and thus tries to avoid this and rather go for the first conversion.
Upvotes: 3