Stuart Leyland-Cole
Stuart Leyland-Cole

Reputation: 1263

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

Upvotes: 5

Views: 32749

Answers (2)

Jon Skeet
Jon Skeet

Reputation: 1500525

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?

Upvotes: 8

Kirill Bestemyanov
Kirill Bestemyanov

Reputation: 11964

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

Upvotes: 3

Related Questions