Ciao Stefano,
quando invochi il metodo AsEnumerable() sull'oggetto di tipo TTable, ti restituisce sempre e comunque un IEnumerable<DataRow>, e non un IEnumerable<TRow>.
Di conseguenza, il metodo Except si trova a lavorare con degli IEnumerable<DataRow> e si aspetta di ricevere come parametro un comparer che sia anch'esso di tipo IEqualityComparer<DataRow>.
Tu invece gli stai passando un IEqualityComparer<TRow> e questo è un problema.
Dalla documentazione su MSDN:
https://msdn.microsoft.com/en-us/library/ms132151%28v=vs.110%29.aspxIl parametro T è
controvariante (lo capisci anche dalla parola chiave
in che si trova di fianco al parametro). Vuol dire che al posto di T puoi usare un tipo base (es. object è un tipo base di DataRow) ma non un tipo derivato (ed ecco perché non va bene passare TRow che è un tipo derivato da DataRow).
Sembra un po' controintuitivo, vero? L'esperienza ci dice che ovunque ci sia un parametro di tipo base (es. DataRow) sia consentito passare un oggetto di tipo derivato (es. TRow). Qui invece sembra vero il contrario.
In realtà prende subito senso se vai a guardare la tua implementazione di Equals, uno dei metodi richiesti dall'interfaccia IEqualitiComparer<T>.
Poniamo per ipotesi che tu abbia creato un'istanza di Class1 indicando il tipo derivato "MioDataRow" per il parametro TRow. L'Equals dunque si aspetta di ricevere come parametri due oggetti di tipo "MioDataRow". Proviamo a visualizzarlo:
public bool Equals(MioDataRow x, MioDataRow y) {
return GetComparerEquals(x, y);
}
Ora, ricorda che l'Except sta lavorando con collezioni di DataRow. Come potrebbe l'Except passare un oggetto di tipo DataRow come argomento al metodo Equals che invece accetta dei MioDataRow, che sono dei tipi più specializzati?
Ovviamente non può, perché non è type-safe a compile time, ed ecco perché hai l'errore.
La soluzione consiste nel far implementare alla tua classe l'interfaccia IEqualityComparer<DataRow> (quindi senza usare il parametro TRow) oppure, se sei sicuro che TTable restituisce delle TRow, scrivi:
int r2 = newData.AsEnumerable().Cast<TRow>().Except(oldData.AsEnumerable().Cast<TRow>(), this).Count();
Qui ho usato il metodo Cast<TRow> per convertire quelle che originariamente erano delle DataRow nel tuo tipo specializzato TRow. Ovviamente se la conversione fallisce tu avrai un errore a runtime.
Come ultima considerazione: penso che non ci sia una reale utilità pratica nel creare Class1 come una classe generica. Come hai visto, l'AsEnumerable ti restituisce comunque dei comuni DataRow. A questo punto puoi semplicemente far lavorare Class1 con i tipi base DataTable e DataRow.
Per approfondire, leggi qui sulla covarianza/controvarianza che sono concetti supportati a partire dal .NET Framework 4.
https://msdn.microsoft.com/en-us/library/dd799517%28v=vs.110%29.aspxciao,
Moreno
Modificato da BrightSoul il 03 febbraio 2015 23.09 -