2010-07-08 14 views
6

Non riesco a capire la logica dietro List<int> in quanto infrange alcune delle regole di base.Elenco <int> in C#

List<int> deve essere di tipo valore e non di tipo di riferimento.

  1. List<int> deve essere passato per ref parola chiave se il suo valore è di essere mantenuta tra chiamate di funzione. Questo significa che sta visualizzando un comportamento di tipo value simile a int.
  2. Ma il List<int> deve essere inizializzato da un nuovo operatore. Anche List<int> potrebbe essere nullo pure. Ciò implica un comportamento di tipo di riferimento.

Un tipo nullable è diverso in quanto non deve essere inizializzato dal nuovo operatore.

Sto vedendo qualcosa di sbagliato qui?

EDITED-

, avrei postato il codice nella domanda iniziale stesso. Ma segue qui -

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      ListTest d = new ListTest(); 
      d.Test(); 
     } 
    } 

    class ListTest 
    { 
     public void ModifyIt(List<int> l) 
     { 
      l = returnList(); 
     } 

     public void Test() 
     { 
      List<int> listIsARefType = new List<int>(); 
      ModifyIt(listIsARefType); 
      Console.WriteLine(listIsARefType.Count); // should have been 1 but is 0 
      Console.ReadKey(true); 
     } 

     public List<int> returnList() 
     { 
      List<int> t = new List<int>(); 
      t.Add(1); 
      return t; 
     } 
    } 
} 
+3

Che cosa intendi esattamente con il tuo punto 1? Puoi dare un esempio perché pensi di aver bisogno di 'ref'? –

+0

@ 0xA3 => crea un metodo1 che utilizza come parametro l'elenco . Quindi nel metodo principale passare il metodo1 a Elenco . Nel metodo1 aggiungi alcuni numeri interi alla lista. Nel metodo principale vedi se riesci a recuperare questi numeri interi. – Prashant

+0

mattmc3 ha già pubblicato un esempio come quello che dimostra che non è necessario 'ref'. –

risposta

27

elenco dovrebbe essere di tipo di valore e non Tipo di riferimento.

Errore!int è un tipo di valore. List<int> è un tipo di riferimento.

+0

Se fosse il caso, perché passare da un riferimento all'Elenco tra le funzioni? – Prashant

+4

@Prash Lo farebbe se si volesse modificare il riferimento stesso (es .: impostarlo su null o su una lista diversa). Ma normalmente non creerei il codice per renderlo necessario. –

+3

@Prashant: non è necessario. Chi ti ha detto che lo fai, è sempre un deficiente. Ciò che la parola chiave 'ref' fa è consentire di impostare il parametro su un altro' Elenco '. Che non è nemmeno così comune come sembra che sia. – cHao

2

List<int> è effettivamente un tipo di riferimento. Ma gli articoli contenuti nella lista sono tipi di valore.

I tipi di nullità tuttavia sono implementati come strutture (struct Nullable<T> where T : struct) e sono quindi tipi di valore. La ragione per cui si può semplicemente scrivere

int? i = 3; 

senza la parola chiave new è che la sintassi di cui sopra è tradotto dal compilatore automaticamente in codice che farà il seguente:

Nullable<Int32> i = new Nullable<Int32>(3); 

Per ottenere una migliore comprensione di le differenze tra tipo di valore e semantica del tipo di riferimento Ti consiglio di leggere l'articolo di Jon Skeet su questo argomento che ti guiderà con un sacco di esempi di codice illustrativo:

Jon Skeet: Parameter passing in C#

1

Un List è un tipo riferimento generico, lo si utilizza con un tipo di valore int. Ma è ancora un tipo di riferimento.

2

Non pensarlo come List<int> pensateci come è stato scritto List<t>.

L'elenco è una classe generica . Non è una struttura. È una classe generica che può funzionare con tipi di valori e tipi di riferimento.

0

List<int> è un tipo di riferimento. E non deve essere passato come riferimento.

Il tipo degli oggetti nel lista è tipo-di un tipo di valore, tranne che gli oggetti tipo valore possono finire scatolato (convertito in oggetti riferimento di tipo di una specie) comunque, questo so..scratch paragrafo una volta capito questo.

+0

Si noti che 'Lista ' non * richiede * la boxe. Solo se si inserisce un tipo di valore in una 'Lista ' la boxe sarà coinvolta. –

+0

@ 0xA3: non ero sicuro di quanto bene .net l'ha fatto. So che Java lo infastidisce, ma i suoi Integers sono un mezzo tentativo di far apparire le primitive come oggetti, mentre i "primitivi" di .net sono oggetti reali. – cHao

7

Penso che tu abbia un'assunzione errata nel tuo primo proiettile. L'oggetto Elenco generico è sicuramente un tipo di riferimento (sull'heap, non sullo stack). Non sei sicuro del motivo per cui pensi di dover passare tramite ref. Questo stampa "2" come dovrebbe:

namespace ConsoleApplication1 { 
    class Program { 
     static void Main(string[] args) { 
     List<int> listIsARefType = new List<int>(); 
     ModifyIt(listIsARefType); 
     ModifyIt(listIsARefType); 
     Console.WriteLine(listIsARefType.Count); // 2! 
     Console.ReadKey(true); 
     } 

     static void ModifyIt(List<int> l) { 
     l.Add(0); 
     } 
    } 
} 
+0

Penso che sia importante notare che qualsiasi Elenco è un tipo di riferimento a causa del fatto che è stato creato da un tipo di base che è un tipo di riferimento. Si noti che un elemento presente nell'elenco non è potenzialmente un tipo di riferimento ma potrebbe diventare un tipo di riferimento. – msarchet

6

è necessario comprendere la differenza tra passaggio per riferimento, passaggio per valore, e riferimento passaggio per valore.

Nel esempio di codice che hai postato, si sta passando il riferimento all'oggetto List<int> da valore. Ciò significa che è possibile mutare l'oggetto indicato dal riferimento e il codice chiamante vedrà queste modifiche. Tuttavia, il riferimento stesso viene passato per valore, quindi se si modifica il riferimento in modo che punti a un oggetto diverso del tutto, il codice chiamante non vedrà le modifiche.

Quando si utilizza la parola chiave ref, si passa il riferimento stesso per riferimento. Ciò significa che non solo è possibile modificare l'oggetto a cui punta il riferimento, ma è anche possibile modificare il riferimento stesso.

Considerate questo esempio:

class Program 
{ 
    static void Main() 
    { 
     int foo = 0; 
     DoSomething1(foo); 
     Console.WriteLine(foo); // Outputs 0. 

     DoSomething1(ref foo); 
     Console.WriteLine(foo); // Outputs 1. 

     var bar = new List<int>(); 
     DoSomething2(bar); 
     Console.WriteLine(bar.Count); // Outputs 1. 

     DoSomething2(ref bar); 
     Console.WriteLine(bar.Count); // Outputs 0. 
    } 

    // Pass by value. 
    static void DoSomething1(int number) 
    { 
     // Can't modify the number! 
     number++; 
    } 

    // Pass by value. 
    static void DoSomething1(ref int number) 
    { 
     // Can modify the number! 
     number++; 
    } 

    // Pass reference by value. 
    static void DoSomething2(List<int> list) 
    { 
     // Can't change the reference, but can mutate the object. 
     list.Add(25); 
    } 

    // Pass reference by reference. 
    static void DoSomething2(ref List<int> list) 
    { 
     // Can change the reference (and mutate the object). 
     list = new List<int>(); 
    } 
} 
0

Oltre alle ipotesi sbagliate trattate in altre risposte, si dicono:

List<int> deve essere inizializzato da un nuovo operatore ... Questo implica un comportamento del tipo di riferimento .

No, in C# l'operatore new è solo la sintassi per chiamare il costruttore di un tipo. Viene utilizzato per entrambi i tipi di valori di riferimento e definiti dall'utente (struct s).

0

Nel metodo ModifyIt(), quando si scrive 'l = returnList()'; 'l' indica ora una posizione diversa nella memoria rispetto a listIsARefType nel metodo Test(). Fondamentalmente scrivendo 'l' '=' qualcosa, hai interrotto il collegamento tra 'l' e 'listIsARefType'. Per mantenere il collegamento (ovvero assicurarsi che entrambi gli oggetti, 'l' e 'listIsARefType' puntino alla stessa posizione nella memoria), è necessario lavorare solo sull'oggetto 'l' (ad esempio chiamando una funzione sull'oggetto) o utilizzare la parola chiave ref nel parametro del metodo ModifyIt().

Problemi correlati