2011-09-27 18 views
18

Poiché Javascript è la lingua con cui sono più esperto, ho familiarità con l'utilizzo di funzioni come oggetti di prima classe. Avevo pensato che al C# mancava questa funzione, ma poi ho sentito parlare di Func e di Action e di delegate, che penso siano piuttosto stupendi.Perché Func non accetta più di 16 argomenti?

Ad esempio, è possibile dichiarare una Func che concatena due stringhe e mette uno spazio tra di loro in questo modo:

Func<string, string, string> concat = (a,b) => a + " " + b; 

Ho notato che quando si digita

Func< 

l'IntelliSense mostra che ha 17 sovraccarichi:

delegate System.Func<out TResult> 
delegate System.Func<in T, out TResult> 
delegate System.Func<in T1, in T2, out TResult> 
...snip... 
delegate System.Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16, out TResult> 

Che mi ha fatto ridere. Ho guardato il MSDN docs per Func e ho riso di nuovo. Questo mi ha fatto provare a dichiarare uno Func con 17 argomenti. Causa un errore (Using the generic type 'System.Func<TResult>' requires 1 type arguments).

Posso essere d'accordo sul fatto che probabilmente non è una buona idea avere uno Func che accetta più di 16 argomenti. Anche così, questo sembra un modo scanzonato per l'implementazione di Func. Richiede 17 sovraccarichi banalmente diversi da documentare. Questo è tutto ciò che dovrebbe davvero essere necessario sapere: l'ultimo parametro di tipo è il tipo di ritorno e tutti i parametri di tipo prima che siano i tipi di argomento.

Quindi cosa posso fare se volevo creare un Func con più di 16 parametri? Perché c'è un limite comunque? Perché C# non può semplicemente dichiarare uno Func con un numero arbitrario di argomenti?

+0

Immagino perché se si provasse ad usare 'params' il tipo restituito verrebbe interpretato come un tipo di argomento. Ma non ho mai capito perché non potrebbe essere "Func " (dato che dichiari una funzione con prototipo (che ha un tipo di ritorno) quindi argomenti (ad esempio 'void Foo (a, b, c)')) –

+3

È solo una semplice dichiarazione. I progettisti .NET si sono stancati dopo le 16. Se hai bisogno di qualcosa di più che dichiarare il tuo Func <>. –

+0

@HansPassant Il punto è, perché devono continuare ad aggiungere sovraccarichi fino a quando non si stancano di esso? Perché non possono semplicemente avere un modo più generale di gestirlo, con un 'out TResult' e' in T' 0 o più volte? –

risposta

16

Si spera in qualcosa come variadic type arguments che C# non ha. C# richiede che venga fissata l'arità dei tipi generici, quindi la proliferazione atroce dei tipi Func, Action e Tuple.

Se stai facendo shopping linguistico, questa funzione è stata aggiunta in C++ 11, ma probabilmente dovresti usare solo jQuery. :-)

+0

Mi piacerebbe vedere come si potrebbe fare una variazione variadica di 'Tuple'! – Gabe

+0

@Gabe: consulta l'articolo di Wikipedia che ho collegato per la sintassi C++. Posso immaginare qualcosa del genere in C#: 'classe Foo {}' (accetta un array di tipi). – Daniel

+0

@Gabe: anche se C# ottiene template variadici, semplicemente non dispone delle funzioni di ottimizzazione per questo. Non eseguirà le funzioni inline che considerano le classi come argomenti, quindi quando provate a costruire una tupla con 16 argomenti otterrete 32 chiamate di funzione mentre C++ le integrerebbe a 0 chiamate di funzione. – Dani

0

Why is there a limit anyway?

C'è parere che funzionerà probabilmente non dovrebbe avere più di 3 argomenti. Se ha di più, diventa sempre più difficile da capire. Naturalmente questo potrebbe non essere il motivo per cui è così in C#, ma questa limitazione potrebbe non essere poi così male.

Direi che anche questo limite di 16 è troppo e incoraggia già scelte di progettazione sbagliate.

14

È possibile definire qualsiasi delegato necessario.Quindi un Func con 20 parametri sarebbe definito in questo modo:

public delegate R Func< 
    P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, 
    P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, R>(
     P0 p0, P1 p1, P2 p2, P3 p3, P4 p4, 
     P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, 
     P10 p10, P11 p11, P12 p12, P13 p13, P14 p14, 
     P15 p15, P16 p16, P17 p17, P18 p18, P19 p19); 

È quindi possibile utilizzare in questo modo:

Func< 
     int, int, int, int, int, int, int, int, int, int, 
     int, int, int, int, int, int, int, int, int, int, int> f = (
      p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, 
      p11, p12, p13, p14, p15, p16, p17, p18, p19) => 
       p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + p10 
        + p11 + p12 + p13 + p14 + p15 + p16 + p17 + p18 + p19; 

var r = f(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); 

C# consente inoltre di utilizzare la sintassi lambda su ogni delegato, così si potrebbe anche fare questo:

public delegate R ArrayFunc<P, R>(params P[] parameters); 

e quindi utilizzarlo in questo modo:

ArrayFunc<int, int> af = ps => ps.Sum(); 

var ar = af(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); 

È una funzionalità molto flessibile e potente della lingua.

+0

Penso che l'OP voglia sapere perché queste variazioni sono necessarie in primo luogo. Mostrargli che puoi proliferare ulteriormente non lo risolve. – Daniel

+3

@Daniel - L'OP ha chiesto come creare un delegato con più di 16 parametri e perché non ha potuto dichiarare un delegato con un numero variabile di parametri. Gli ho mostrato come fare entrambi. A parte questo, non vi è altra ragione se non una scelta arbitraria sul motivo per cui il framework si è fermato a 16. In .NET 2.0 'Func' aveva solo quattro parametri. – Enigmativity

5

Penso di capire - ciò che si può fare con JavaScript e le funzioni (argomenti) è ordinario, ma non è anche tipizzato in modo statico.

Ma si ricorda che non è mai necessario più di un argomento nella programmazione funzionale. Puoi concatenare il numero di argomenti che desideri, restituendo un'altra funzione (questo è un tratto comune in FP e pesantemente usato con la conversione di una tecnica disponibile anche in JS ma solo con una leggera flessione del sistema).

Naturalmente questo è ackward in C#:

Func<A1,Func<A2,Func<A3,...<Func<An,Result>>...> 
    x1 => 
    (x2 => 
     (x3 => 
     ... 
      (xn => 
       { /*return soomething */ } 
))...); 

ma questo è ciò che F # è per;) e, naturalmente, si dovrebbe mai fare una funzione con più di un paio di argomenti (ben al di sotto 16) in ogni caso! .

+2

Sono d'accordo: _ "questo è ciò che F # è per" _ – Daniel

1

I delegati System.Func ci sono probabilmente grazie al team BCL. Chi ha realizzato un numero finito di delegati generici predefiniti sarebbe utile (e persino richiesto per molte situazioni).

Per fare ciò che dici ... cioè un numero illimitato di parametri generici per un delegato Func richiederebbe un cambio di lingua .. la responsabilità riguarderebbe sia il team C# che i team vb.net (e gli altri probabilmente) per cambiare la lingua per permettere questo.

Forse, a un certo punto, se il vantaggio di questa funzione supera il costo di predefinire una manciata di delegati Func e questo è più importante di altre modifiche alla lingua (e che non è un cambiamento irrisorio) le squadre pertinenti implementare parametri generici illimitati .. potrebbe non essere per un po 'però!

Problemi correlati