2009-04-03 3 views
23

Ho una sequenza di funzioni che sono molto simili, ma per una singola linea, come i seguenti due (ma ho molti più di essi):Passando un delegato con due parametri in funzione parametro

private static int HowManyHoursInTheFirstYear(IList<T> samples) 
{ 
    DateTime firstDate = samples[0].Date; 
    int count = 0; 

    while (count < samples.Count && 
      samples[count].Date.Year == firstDate.Year) 
    { 
     count++; 
    } 

    return count; 
} 


private static int HowManyDaysInTheFirstMonth(IList<T> samples) 
{ 
    DateTime firstDate = samples[0].Date; 
    int count = 0; 

    while (count < samples.Count && 
      samples[count].Date.Month == firstDate.Month) // <--- only change! 
     count++; 
    } 

    return count; 
} 

stavo pensando di utilizzare delegati per eliminare questa ripetizione nel codice in qualche modo elegante, che avrebbe permesso di invoco qualcosa come:

HowManyDaysInTheFirstPeriod(
    samples, 
    delegate(DateTime d1, DateTime d2) { return d1.Month == d2.Month; }); 

dichiarando in tal modo un delegato come la seguente:

delegate bool DateComparer(DateTime first, DateTime second); 

e dove HowManyDaysInTheFirstPeriod whould essere qualcosa di simile a quanto segue:

private static int HowManySamplesInFirstPeriod 
    IList<T> samples, 
    DateComparer comparer) 
{ 
    DateTime firstDate = samples[0].Date; 
    int count = 0; 

    while (count < samples.Count && comparer()) 
    { 
     count++; 
    } 
} 

Purtroppo, il compilatore si lamenta che di confronto ha bisogno di due parametri.

Sono relativamente nuovo a C# e colpisco un blocco stradale qui. Come risolverebbe questo?

+3

BTW, per qualcuno che dice che sono abbastanza nuovo di C#, questo è un uso molto ragionevole dei delegati. Complimenti a te per aver individuato la ripetizione e averla trasformata in un delegato. –

risposta

14

Sei quasi arrivato! Il parametro delegato comparer è uguale a qualsiasi altra funzione: È ancora necessario passare gli argomenti appropriati per richiamarlo. Nel tuo caso, che sta andando a significare questo cambiamento:

while (count < samples.Count && comparer(samples[count].Date, firstDate)) 
{ 
    count++; 
} 

(. Si noti inoltre che samples dovrebbe probabilmente essere samples.Count, come ho scritto sopra)

4

È necessario passare al confronto le due date in questione. E 'probabilmente si protende semplice come:

private static int HowManySamplesInFirstPeriod 
    IList<T> samples, 
    DateComparer comparer) 
{ 
    DateTime firstDate = samples[0].Date; 
    int count = 0; 

    while (count < samples.Count 
      && comparer(samples[count].Date, firstDate)) 
    { 
     count++; 
    } 
} 

Oppure si può decidere di passare loro il contrario (cioè firstDate e poi samples[count].Date).

0

È necessario passare le date confrontate al delegato. Quindi:

comparer(samples[count].Date, firstDate) 
6

si può fare un po 'più semplice . Basta fornire la funzione di un delegato che estrae tutto ciò dovrebbe essere confrontato da un DateTime:

private static int HowManySamplesInFirstPeriod<T> 
    IList<T> samples, 
    Func<DateTime, int> f // a function which takes a DateTime, and returns some number 
{ 
    DateTime firstDate = samples[0].Date; 
    int count = 0; 

    while (count < samples && f(samples[count].Date) == f(firstDate)) 
    { 
     count++; 
    } 
} 

e può quindi essere chiamato come tale:

HowManySamplesInFirstPeriod(samples, (dt) => dt.Year); // to get the year 
HowManySamplesInFirstPeriod(samples, (dt) => dt.Month); // to get the month 

La sintassi (dt) => dt.year può essere una novità per voi, ma è un modo più pulito di scrivere "un delegato anonimo che prende un oggetto dt di un tipo generico e restituisce dt.year". Si potrebbe invece scrivere un delegato vecchio stile, ma questo è più bello.:)

Possiamo fare un po 'più generale di ciò, però, con l'aggiunta di un altro parametro di tipo generico:

private static int HowManySamplesInFirstPeriod<T, U> 
    IList<T> samples, 
    Func<DateTime, U> f // Let's generalize it a bit, since the function may return something other than int (some of the DateTime members return doubles, as I recall) 

Come sempre però, LINQ fornisce una migliore alternativa ancora:

private static int HowManySamplesInFirstPeriod<T> 
    IList<T> samples, 
    Func<DateTime, int> f) 
{ 
    var firstVal = f(samples.First().Date); 
    return samples.Count(dt => f(dt.Date) = firstVal) 
} 
1

Penso che la risposta di Jalf debba essere leggermente modificata per adattarsi all'utilizzo originale:

private static int HowManyHoursInTheFirstYear(IList<DateTime> samples, Func<DateTime, DateTime, bool> comparer) 
{ 
    DateTime firstDate = samples[0].Date; 
    int count = 0; 
    while (count < samples.Count && comparer(samples[count], firstDate)) { 
     count++; 
    } 
    return count; 
} 

Chiama usando:

HowManyDaysInTheFirstPeriod(samples, (d1, d2) = > { return d1.Month == d2.Month; }); 
HowManyDaysInTheFirstPeriod(samples, (d1, d2) = > { return d1.Year == d2.Year; });