2013-04-07 22 views
6

Ho problemi con generici ed ereditarietà. Ho una classe astratta chiamata CriteriaBase il cui compito è determinare se un'entità T corrisponde ai criteri definiti in qualsiasi sottoclasse. Le sottoclassi devono implementare un metodo che restituisce un Func che rappresenta i criteri. Il problema sorge quando provo a utilizzare i generici per lo Func. Spero che qualche codice possa illustrare il mio problema.'Gli argomenti di tipo non possono essere dedotti dall'uso'

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

La mia intenzione è che la classe CriteriaBase dovrebbe essere generico e completamente riutilizzabile.

Un esempio sottoclasse:

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

Il problema è nel metodo GetNumberOfCriteriaMatches(). L'opzione 2 è come ho originariamente scritto il codice, ma ottengo l'errore di compilazione come elencato. Se utilizzo l'opzione 1, il codice viene compilato ma significa che quando eseguo l'override di Criteria nella sottoclasse, devo utilizzare IChromosome anziché MatchDay che non funziona (devo accedere a funzioni specifiche di MatchDay). Nella mia mente semplice, le opzioni 1 e 2 sono equivalenti. L'opzione 2 sostituisce semplicemente IChromosome con un tipo generico U che è limitato a una classe che implementa IChromosome.

È ciò che sto cercando di ottenere possibile? In caso affermativo, cosa mi manca/incomprensione? In caso contrario, come dovrei affrontare questo problema?

Per completezza (incluso alla fine, come io non sono sicuro di quanto si aiuta con la domanda), ecco le due entità che sono attualmente in uso per la T (Season) e 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; } 
    } 
} 
+0

Quale versione di.quadro di rete usi? –

+0

. NET versione 4: la versione completa non è il profilo del client. La risposta è diversa a seconda della versione? Utilizzando Visual Studio 2010 è interessante. – Stu

+0

La covarianza e la contravarianza dei delegati funziona in C# dalla versione 4.0 –

risposta

8

Ebbene questo è il problema:

public IEnumerable<IChromosome> Chromosomes 

sei solo dichiarando che si sta tornando una sequenza di IChromosome valori. Il tuo criterio prevede i valori MatchDay. Si sa che in realtà sta restituendo una sequenza di valori MatchDay, ma il compilatore no.

Si potrebbe utilizzare Cast<> di controllare questo al momento dell'esecuzione:

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

... o si potrebbe cambiare Chromosomes per restituire un IEnumerable<MatchDay>. Sfortunatamente non è possibile stabilire se questa sia una risposta valida o meno, in quanto non sappiamo come viene dichiarato ICrossoverable. Forse dovresti rendere lo standard ICrossoverable generico nel tipo di elemento?

+0

Ah ah! Questo lo risolve. Penso che la soluzione reale risieda nell'ultima frase: rendere generico 'ICrossoverable'. Ci proverò domani e riferirò. – Stu

+2

Come suggerito, ho creato un generico 'ICrossoverable' che ha risolto il mio problema. Il codice è in [mio repository Github] (https://github.com/hungryhippo2312/GeneticAlgorithm). Ho rinominato ICrossoverable in IOrganism perché si adatta molto meglio alla terminologia dell'algoritmo genetico. Un'implementazione di "CriteriaBase può essere trovata [in un altro repository Github] (https://github.com/hungryhippo2312/FixtureGenerator/blob/master/FixtureGenerator/Criteria/SuperSundayCriteria.cs). Grazie per i molteplici e vari suggerimenti. – Stu

3

È necessario utilizzare la parola chiave in prima di U nella definizione di CriteriaBase. Qualcosa di simile a questo:

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

Aggiornamento. Non funzionerà. Prova a specificare il tipo in modo esplicito

private int GetNumberOfCriteriaMatches(T season) 
{ 
.... 
    return season.Chromosomes.Count<IChromosome>(Criteria); 
} 
+0

Quando faccio questo, viene visualizzato un errore del compilatore che dice "I parametri del tipo di variabile possono essere dichiarati solo nelle interfacce o nei delegati". Tre domande: cosa significa aggiungere 'in', cosa significa questo errore e perché non funziona? Grazie! – Stu

+0

Ho dimenticato che questo potrebbe funzionare solo per i delegati e le interfacce. Ok. Quindi prova a specificare esplicitamente il conteggio (criterio) –

+0

Ora hai un errore diverso! 'Tipo di argomento' System.Func 'non è assegnabile al tipo di parametro' System.Func '. È quasi come se "dove U: IChromosome" non esiste! Dove ora? – Stu

Problemi correlati