6

In un programma C# ho creato un metodo che elimina un oggetto da un elenco. L'utente inserisce l'indice dell'elemento da eliminare, l'utente viene quindi chiesto di confermare l'eliminazione e l'elemento viene rimosso dall'elenco se l'utente conferma, altrimenti l'elenco rimane lo stesso.
Non sono sicuro del modo migliore per passare argomenti al metodo. Ho provato a passare la lista di riferimento (come parametro out):In C# devo passare un parametro per valore e restituire la stessa variabile o passare per riferimento?

static void DeleteCustomer(out List<Customer> customers) 
{ 
    // ...display list of objects for user to choose from... 
    int deleteId = ReadInt("Enter ID of customer to delete: "); 
    Console.Write("Are you sure you want to delete this customer?"); 
    if (Console.ReadLine().ToLower() == "y") 
    { 
     customers.RemoveAt(deleteId); 
    } 
} 

Il codice di cui sopra non funziona come ricevo gli errori Uso di variabili locali non assegnata 'clienti' e il parametro OUT clienti' 'deve essere assegnato a prima che il controllo lasci il metodo corrente. Stavo pensando che potrei passare la lista per valore e restituire la stessa lista, in questo modo:

static List<Customer> DeleteCustomer(List<Customer> customers) 
{ 
    int deleteId = ReadInt("Enter ID of customer to delete: "); 
    Console.Write("Are you sure you want to delete this customer?"); 
    if (Console.ReadLine().ToLower() == "y") 
    { 
     customers.RemoveAt(deleteId); 
    } 
    return customers; 
} 

// ...which would be called from another method with: 
List<Customer> customers = DeleteCustomer(customers); 

, ma questo non sembra efficiente come la stessa variabile viene passato per valore e poi restituito.

Qual è il modo più efficiente per passare argomenti in questo caso?

+1

L'elenco viene passato automaticamente in base al riferimento, quindi non è necessario restituirlo. Il metodo di chiamata vedrà la modifica riflessa nel suo riferimento alla lista passata. – user469104

+2

Vedere: [Parametro che passa in C# - Da Jon Skeet] (http://www.yoda.arachsys.com/csharp/parameters.html). Prova anche il secondo metodo, senza restituire l'elenco, vedrai che è stato modificato. Questo perché l'indirizzo del tipo di riferimento è passato per valore. ** Ma soprattutto **, non dovresti preoccuparti dell'efficienza * ora *, prendi in considerazione la scrittura di un codice che trasmetta le intenzioni in modo più chiaro e solo successivamente cerchi di migliorare le prestazioni. – Habib

+0

Il secondo metodo potrebbe avere un tipo di ritorno 'void'. –

risposta

4

List come tutti i tipi di riferimento, viene passato come riferimento all'oggetto, e non una copia.

Si noti che questo è molto diverso dal dire si passa da di riferimento, in quanto ciò implicherebbe l'assegnazione del parametro si propaga al chiamante, che non è così

Lo fa significa che le modifiche al oggetto (come quelli eseguiti da RemoveAt) si propagherà automaticamente al chiamante.

Così, basta passarlo; non è necessario un valore di ritorno o i parametri out/ref.

Sarà molto raramente utilizza out/ref per i tipi di riferimento, e quando viene utilizzato per i tipi di valore, la differenza di prestazioni sarà così piccola e di ritorno che non si deve preoccupare se non si ha profilato e reso sicuro che il problema si verifica lì. Usa ciò che rende il senso più idiomatico.

+2

'Elenco come tutti i tipi di riferimento, è passato come riferimento all'oggetto, e non una sua copia. Ciò significa che le modifiche si propagheranno automaticamente al chiamante ». Questo non è corretto. Non è possibile assegnare "null" o una nuova istanza all'oggetto passato. L'indirizzo del tipo di riferimento viene passato per valore. – Habib

+1

@Habib Totalmente d'accordo. È passato * come * un riferimento; non * per * riferimento. Passare un riferimento per riferimento è diverso. Hai una formulazione che ha più senso per te? A volte vorrei davvero avere dei puntatori, è molto più facile da spiegare ... Ho anche modificato (mentre stavi scrivendo?) Per chiarire la dichiarazione di propagazione. – BradleyDotNET

+3

È una dichiarazione complicata, sono più incline a dire che nulla in C# viene passato per riferimento a meno che non venga usata la parola chiave ref/out. Per i tipi di riferimento, il riferimento/indirizzo viene passato come valore. – Habib

2

In C# il parametro viene passato per valore. Ciò significa che quando si passa un parametro a un metodo, viene passata una copia del parametro. C# ha tipi per valore (come int) e per riferimento (come qualsiasi classe). C# contiene uno stack (quando spinga tutti i varaibles) e un Heap. Il valore dei tipi di valore spinge direttamente in questo stack, mentre il riferimento del tipo di riferimento viene inserito nello stack e il valore di riferimento viene inserito nell'Heap.
Quando si passa un tipo di riferimento (come un elenco), si effettua una copia del riferimento, ma questa copia punta allo stesso oggetto nell'elenco. Pertanto qualsiasi cambiamento influisce direttamente sull'oggetto, a meno che tu non modifichi il riferimento (con un assigmet), ma questo non è il tuo caso.

questo potrebbe dal codice:

static void DeleteCustomer<T>(List<T> customers) 
    { 
     Console.WriteLine("Enter ID of customer to delete: "); 
     int deleteId; 
     if (int.TryParse(Console.ReadLine(), out deleteId)) // if the input is an int 
     { 
      Console.Write("Are you sure you want to delete this customer?"); 
      if (Console.ReadLine().ToLower() == "y") 
      { 
       customers.RemoveAt(deleteId); 
      } 
     } 
     else 
     { 
      Console.WriteLine("This is not valid Id"); 
     } 
    } 

Se vuoi sapere di Rif una parola chiave out posso aiutare anche voi, ma per questo esempio non è neccesary.