2012-05-09 20 views
6

Il codice seguente è valida:Assegnare una funzione di delegato che restituisce un tipo anonimo a una variabile

IEnumerable<SomeThing> things = ...; 

// map type SomeThing to a new anonymous type, resulting in a strongly typed 
// sequence based on an anon type 

var newList = things.Select(item => 
    { 
     return new 
     { 
      ID = item.ID, 
      DateUpdatedOrCreated = ((DateTime)(item.DateUpdated ?? 
        item.DateCreated)).ToShortDateString(), 
      Total = item.Part1 + item.Part2 
     }; 
    }); 

newList appare ora in Visual Studio come IEnumerable<'a> ed è fortemente tipizzato con il tipo anonimo creato nella funzione. È così bello

Quello che non riesco a fare è trovare un modo per assegnare solo l'espressione lambda (e non l'enumerazione) a una variabile implicitamente tipizzata. Anche se il compilatore non ha alcun problema con il tipo anonimo nel contesto di cui sopra, se provo (diciamo)

var func = (SomeThing item)=> { 
     return new { ... }; 
    }; 

ottengo l'errore "non può assegnare espressione lambda al implicitamente-digitato variabile locale". Questa sembra una strana limitazione del compilatore; a meno che non mi manchi qualcosa, i tipi sono altrettanto non ambigui nel secondo esempio come sono nel primo: entrambi i parametri di tipo sono ben definiti.

C'è un modo per farlo? Dal momento che è un tipo anonimo, ovviamente, non ho modo di usare un tipo per assegnarlo esplicitamente, quindi sembra che sarei bloccato a fare una classe per il tipo di output, se non lo fosse.

Aggiornamento

Poco dopo essere andato sul mio modo allegro con la risposta di Jon Skeet, ho trovato un dilemma simile istanziare le classi. Nel caso non sia ovvio, lo stesso trucco può essere utilizzato per creare classi fortemente tipizzate utilizzando tipi anonimi dedotti.

class Processor<T,U> 
{ 
    public Processor(Func<T,U> func) { 

    } 
} 

// func is a delegate with anon return type created using method in answer below 

var instance = new Processor(func); // does not compile! Requires type arguments! 

non possono essere creati direttamente, ma possono essere creati più o meno allo stesso modo in cui il trucco di seguito:

public static Processor<T,U> Create<T,U>(Func<T,U> func) { 
    return new Processor<T,U>(func); 
} 

var instance = Processor.Create(func); // all good 

risposta

8

lo si può fare tramite l'inferenza di tipo:

var func = BuildFunc((SomeThing item) => { 
    return new { ... }; 
}); 

... 

static Func<TSource, TResult> BuildFunc<TSource, TResult>(
    Func<TSource, TResult> function) { 
    return function; 
} 

Si noti che BuildFunc in realtà non fa nulla: fornisce solo la chiamata al metodo necessaria per fare in modo che il compilatore faccia inferenza di tipo per gli argomenti di tipo generico per Func<,> - aggiunge le informazioni che t sei interessato a Func<,>, in pratica, ovvero informazioni che non possono essere specificate come parte di una dichiarazione di variabile, senza specificare anche gli argomenti di tipo.

+0

Questo funziona perché l'inferenza di tipo per generici è più intelligente dell'inferenza di tipo per 'var'. (Presumo che jon potrebbe essere in grado di spiegare il perché, anche se non posso.) – Servy

+0

Buona risposta. Informazioni aggiuntive: Funziona perché non è necessario specificare il tipo di variabile qui. Il compilatore non può solo inferire il tipo per la variabile perché ci sono più tipi di delegati tra cui potrebbe scegliere. – usr

+0

@Servy: Non si tratta di essere più intelligenti in quanto tali, è una questione di essere in grado di specificare che si desidera un delegato 'Func', ma specificandolo in modo generico. –

Problemi correlati