'The type arguments cannot be inferred from the usage'

32,472

Solution 1

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?

Solution 2

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);
}
Share:
32,472
Stuart Leyland-Cole
Author by

Stuart Leyland-Cole

Updated on April 09, 2020

Comments

  • Stuart Leyland-Cole
    Stuart Leyland-Cole about 4 years

    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; }
        }
    }
    
  • Stuart Leyland-Cole
    Stuart Leyland-Cole about 11 years
    When I do this I get a compiler error saying 'Variant type parameters could be declared in interfaces or delegates only'. Three questions: what does adding in do, what does this error mean and why doesn't it work? Thanks!
  • Kirill Bestemyanov
    Kirill Bestemyanov about 11 years
    I forget that this could works only for delegates and interfaces. Ok. Than try to specify explicitly Count<IChromosome>(Criteria)
  • Stuart Leyland-Cole
    Stuart Leyland-Cole about 11 years
    Now have a different error! 'Argument type 'System.Func<U, bool>' is not assignable to parameter type 'System.Func<GeneticAlgorithm.IChromosome, bool>'. It's almost as if 'where U : IChromosome' doesn't exist! Where now?
  • Kirill Bestemyanov
    Kirill Bestemyanov about 11 years
    I have no new ideas... Try to summon Jon Skeet here.
  • Stuart Leyland-Cole
    Stuart Leyland-Cole about 11 years
    Ah ha! That clears it up. I think the actual solution lies in your last sentence - make ICrossoverable generic. I will try this tomorrow and report back.
  • Stuart Leyland-Cole
    Stuart Leyland-Cole about 11 years
    As suggested, I made ICrossoverable generic which solved my problem. The code is in my Github repository. I renamed ICrossoverable to IOrganism as it fits much better with the genetic algorithm terminology. An implementation of `CriteriaBase<TOrganism, TChromosome> can be found in another Github repository. Thanks for the multiple and varied suggestions.