Funzionalità di C# che supportano LINQ
Espressioni di query
Le espressioni di query usano una sintassi dichiarativa simile a SQL o XQuery per eseguire una query sulle raccolte System.Collections.Generic.IEnumerable<T>. In fase di compilazione, la sintassi di query viene convertita in chiamate al metodo per l'implementazione di un provider LINQ dei metodi di query standard. Le applicazioni controllano gli operatori di query standard inclusi nell'ambito specificando lo spazio dei nomi adatto con una direttiva using
. Nell'espressione di query seguente viene usata una matrice di stringhe, raggruppate in base al primo carattere della stringa, e vengono ordinati i gruppi.
var query = from str in stringArray
group str by str[0] into stringGroup
orderby stringGroup.Key
select stringGroup;
Variabili tipizzate in modo implicito (var)
È possibile usare il modificatore var per indicare al compilatore di dedurre e assegnare il tipo, come illustrato di seguito:
var number = 5;
var name = "Virginia";
var query = from str in stringArray
where str[0] == 'm'
select str;
Le variabili dichiarate come var
sono fortemente tipizzate, esattamente come le variabili in cui il tipo viene specificato in modo esplicito. L'uso di var
consente di creare tipi anonimi, ma solo per le variabili locali. Per altre informazioni, vedere Variabili locali tipizzate in modo implicito.
Inizializzatori di oggetto e di raccolta
Gli inizializzatori di oggetti e Collection consentono di inizializzare gli oggetti senza chiamare in modo esplicito un costruttore per l'oggetto. Gli inizializzatori vengono usati in genere nelle espressioni di query quando proiettano i dati di origine in un nuovo tipo di dati. Supponendo di usare una classe denominata Customer
con le proprietà pubbliche Name
e Phone
, l'inizializzatore di oggetto può essere usato come nel codice seguente:
var cust = new Customer { Name = "Mike", Phone = "555-1212" };
Continuando con la classe Customer
, si supponga che sia presente un'origine dati denominata IncomingOrders
e che per ogni ordine con OrderSize
di grandi dimensioni si voglia creare un nuovo oggetto Customer
basato su tale ordine. È possibile eseguire una query LINQ su questa origine dati e usare l'inizializzazione di oggetti per completare una raccolta:
var newLargeOrderCustomers = from o in IncomingOrders
where o.OrderSize > 5
select new Customer { Name = o.Name, Phone = o.Phone };
L'origine dati può avere più proprietà definite rispetto alla classe Customer
, ad esempio OrderSize
, ma, con l'inizializzazione di oggetti, i dati restituiti dalla query vengono modellati nel tipo di dati desiderato. Si scelgono quindi i dati pertinenti per la classe. Di conseguenza, ora si ha un System.Collections.Generic.IEnumerable<T> riempito con i nuovi Customer
desiderati. L'esempio precedente può anche essere scritto nella sintassi del metodo LINQ:
var newLargeOrderCustomers = IncomingOrders.Where(x => x.OrderSize > 5).Select(y => new Customer { Name = y.Name, Phone = y.Phone });
A partire da C# 12, è possibile usare un'espressione di raccolta per inizializzare una raccolta.
Per altre informazioni, vedi:
- Inizializzatori di oggetto e di raccolta
- Sintassi di espressione della query per operatori di query standard
Tipi anonimi
Il compilatore costruisce un tipo anonimo. Il nome del tipo è disponibile solo per il compilatore. I tipi anonimi costituiscono una valida soluzione per raggruppare temporaneamente un set di proprietà nel risultato di una query senza dovere definire un tipo denominato separato. I tipi anonimi vengono inizializzati con una nuova espressione e un inizializzatore di oggetto, come illustrato di seguito:
select new {name = cust.Name, phone = cust.Phone};
A partire da C# 7, è possibile usare le tuple per creare tipi senza nome.
Metodi di estensione
Un metodo di estensione è un metodo statico che può essere associato a un tipo, in modo da poter essere chiamato come se fosse un metodo di istanza sul tipo. Questa funzionalità consente in pratica di "aggiungere" nuovi metodi ai tipi esistenti senza doverli effettivamente modificare. Gli operatori query standard sono un set di metodi di estensione che forniscono funzionalità di query LINQ per qualsiasi tipo che implementa IEnumerable<T>.
Espressioni lambda
Un'espressione lambda è una funzione inline che usa l'operatore =>
per separare i parametri di input dal corpo della funzione e, in fase di compilazione, può essere convertita in un delegato o in un albero delle espressioni. Nella programmazione LINQ, le espressioni lambda vengono usate quando si effettuano chiamate al metodo dirette agli operatori di query standard.
Espressioni come dati
Gli oggetti di query sono componibili, vale a dire che è possibile ottenere una query da un metodo. Gli oggetti che rappresentano le query non memorizzano la raccolta risultante, ma piuttosto i passaggi da eseguire per ottenere i risultati quando necessario. Il vantaggio di ottenere oggetti query dai metodi è che gli oggetti possono essere ulteriormente composti o modificati. Di conseguenza, qualsiasi valore restituito o parametro out
di un metodo che restituisce una query deve contenere anche quel tipo. Se un metodo materializza una query in un oggetto List<T> concreto o un tipo Array, restituisce i risultati della query anziché la query stessa. Una variabile di query che viene restituita da un metodo può ancora essere composta o modificata.
Nell'esempio seguente il primo metodo QueryMethod1
restituisce una query come valore restituito e il secondo metodo QueryMethod2
restituisce una query come parametro out
(returnQ
nell'esempio). In entrambi i casi viene restituita una query, non i risultati della query.
IEnumerable<string> QueryMethod1(int[] ints) =>
from i in ints
where i > 4
select i.ToString();
void QueryMethod2(int[] ints, out IEnumerable<string> returnQ) =>
returnQ =
from i in ints
where i < 4
select i.ToString();
int[] nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
var myQuery1 = QueryMethod1(nums);
La query myQuery1
viene eseguita nel ciclo foreach seguente.
foreach (var s in myQuery1)
{
Console.WriteLine(s);
}
Posizionare il puntatore del mouse su myQuery1
per visualizzarne il tipo.
È anche possibile eseguire la query restituita direttamente da QueryMethod1
senza usare myQuery1
.
foreach (var s in QueryMethod1(nums))
{
Console.WriteLine(s);
}
Posizionare il puntatore del mouse sulla chiamata a QueryMethod1
per visualizzare il tipo restituito.
QueryMethod2
restituisce una query come valore del parametro out
:
QueryMethod2(nums, out IEnumerable<string> myQuery2);
// Execute the returned query.
foreach (var s in myQuery2)
{
Console.WriteLine(s);
}
È possibile modificare una query usando la composizione di query. In questo caso, l'oggetto query precedente viene usato per creare un nuovo oggetto query. Questo nuovo oggetto restituisce risultati diversi rispetto all'oggetto query originale.
myQuery1 =
from item in myQuery1
orderby item descending
select item;
// Execute the modified query.
Console.WriteLine("\nResults of executing modified myQuery1:");
foreach (var s in myQuery1)
{
Console.WriteLine(s);
}