Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'The type arguments cannot be inferred from the usage'

I am having some troubles with generics and inheritance. I have an abstract class called CriteriaBase whose job it is to determine if an entity T matches the criteria defined in any sub-classes. The sub-classes have to implement a method which returns a Func representing the criteria. The problem arises when I try to use generics for the Func. Hopefully some code will illustrate my problem.

public abstract class CriteriaBase<T, U>
    where T : ICrossoverable
    where U : IChromosome
{
    protected abstract Func<U, bool> Criteria { get; }
    //some code removed for brevity

    private int GetNumberOfCriteriaMatches(T season)
    {
        //1.  works
        //Func<IChromosome, bool> predicate = c => c.Genes == null;
        //return season.Chromosomes.Count(predicate);

        //2.  doesn't work - The type arguments for method 'int 
        //System.Linq.Enumerable.Count<TSource>(this IEnumerable<TSource>, 
        //Func<TSource, bool>)'
        //cannot be inferred from the usage. Try specifying the type arguments 
        //explicitly.
        return season.Chromosomes.Count(Criteria);
    }
}

My intention is that the CriteriaBase class should be generic and completely reusable.

An example sub-class:

public class TopTeamsPlayingEachOtherCriteria : CriteriaBase<Season, MatchDay>
{
    //some code removed for brevity

    protected override Func<MatchDay, bool> Criteria
    {
        get { return matchDay =>
            matchDay.Fixtures.Count(
                fixture =>
                fixture.HomeTeam.TableGrouping.Ordering == 1 
                && fixture.AwayTeam.TableGrouping.Ordering == 1) > 1; }
    }
}

The problem is in the GetNumberOfCriteriaMatches() method. Option 2 is how I originally wrote the code but I get the compile error as listed. If I use option 1 then the code compiles but it means that when I override Criteria in the sub-class, I have to use IChromosome instead of MatchDay which doesn't work (I need to access specific features of a MatchDay). In my simple mind, options 1 and 2 are equivalent. Option 2 simply replaces IChromosome with a generic type U which is restricted to a class that implements IChromosome.

Is what I'm trying to achieve possible? If so, what am I missing/misunderstanding? If not, how should I approach this problem?

For completeness (included at the end as I'm not sure how much it helps with the question), here are the two entities that I'm currently using for T (Season) and U (MatchDay).

public class Season : ICrossoverable
{
    private readonly IEnumerable<MatchDay> _matchDays;

    public Season(IEnumerable<MatchDay> matchDays)
    {
        _matchDays = matchDays;
    }

    public IEnumerable<MatchDay> MatchDays
    {
        get { return _matchDays; }
    }

    //ICrossoverable implementation
    public IEnumerable<IChromosome> Chromosomes
    {
        get { return _matchDays; }
    }
}

public class MatchDay : IChromosome
{
    private readonly int _week;
    private readonly List<Fixture> _fixtures;

    public MatchDay(int week, List<Fixture> fixtures)
    {
        _week = week;
        _fixtures = fixtures;
    }

    //some code removed for brevity

    public IEnumerable<Fixture> Fixtures
    {
        get { return _fixtures; }
    }

    //IChromosome implementation
    public IEnumerable<IGene> Genes
    {
        get { return Fixtures; }
    }
}
like image 718
Stuart Leyland-Cole Avatar asked Apr 07 '13 21:04

Stuart Leyland-Cole


2 Answers

Well this is the problem:

public IEnumerable<IChromosome> Chromosomes

You're only declaring that you're returning a sequence of IChromosome values. Your criterion expects MatchDay values. You happen to know that it's actually returning a sequence of MatchDay values, but the compiler doesn't.

You could use Cast<> to check this at execution time:

return season.Chromosomes.Cast<U>().Count(Criteria);

... or you could change Chromosomes to return an IEnumerable<MatchDay>. Unfortunately we can't really tell whether that's a valid answer or not as we don't know how ICrossoverable is declared. Perhaps you should make ICrossoverable generic in the element type?

like image 199
Jon Skeet Avatar answered Sep 28 '22 05:09

Jon Skeet


You should use keyword in before U in CriteriaBase definition. Something like this:

public abstract class CriteriaBase<T, in U>
    where T : ICrossoverable
    where U : IChromosome

Update. It will not work. Try to specify type explicitly

private int GetNumberOfCriteriaMatches(T season)
{
 ....
    return season.Chromosomes.Count<IChromosome>(Criteria);
}
like image 35
Kirill Bestemyanov Avatar answered Sep 28 '22 05:09

Kirill Bestemyanov