Condividi tramite


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:

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);
}