2011-09-04 13 views
7
namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      object[] obj = new object[3]; 
      obj[0] = new object(); 
      obj[1] = "some string"; 
      obj[2] = 10; 

      string[] strings = new string[] { "one", "two", "three" }; 
      obj = strings; //---> No Error here, Why ? 

      int[] ints = new int[] { 1, 2, 3 }; 
      obj = ints; /*-> Compiler error - Cannot implicitly convert type 'int[]' to 'object[]', Why ?*/ 
     } 
    } 
} 

Wystąpił błąd kompilatora podczas wykonywania kroku, jak pokazano powyżej. Ale w poprzednim kroku nie ma błędu. Czy ktoś może mi wyjaśnić to zachowanie? Używam VS 2010.C# - Błąd kompilatora - podczas przypisywania int [] do obiektu []

EDYCJA - Dla zapewnienia kompletności, znowu, to nie będzie kompilacji - Wsparcie wariancji w .NET 4.0 zostało wyczyszczone teraz. Można użyć nowych słów kluczowych w i z z ogólnymi parametrami typu.

List<object> objectList = new List<object>(); 
    List<string> stringList = new List<string>(); 
    objectList = stringList; 
+2

Rozumiesz różnice między typ wartości i typy referencyjne? – Oded

Odpowiedz

6

tylko tablice typów referencyjnych (np String) mogą być przypisane do tablic innych typów referencyjnych (jak Object). Ponieważ int jest typem wartości, jego tablice mogą nie być przypisane do tablic innych typów.

Aby być bardziej szczegółowym, nazywa się to array covariance. Działa tylko wtedy, gdy wzory bitowe przechowywane w tablicy są zgodne z typem docelowym. Na przykład bity w String[] są odniesieniami do łańcuchów i mogą być bezpiecznie kopiowane do lokalizacji pamięci przechowujących odniesienia do obiektów. Tablica typów wartości przechowuje jednak rzeczywiste dane elementów (w przeciwieństwie do samych odniesień do nich). Oznacza to, że int[] przechowuje rzeczywiste 32-bitowe liczby całkowite w elementach tablicy. Ponieważ 32-bitowa liczba całkowita nie może być bezpiecznie skopiowana do lokalizacji pamięci przechowującej odniesienie do obiektu lub dowolnego innego typu, nie można przypisać ich tablicy do tablicy dowolnego innego typu.

Należy zauważyć, że technicznie bity z int można bezpiecznie skopiować do lokalizacji pamięci przechowującej uint (i na odwrót). Oznacza to, że powinieneś być w stanie zrobić coś w rodzaju int[] x = new uint[10]. To w rzeczywistości nie jest kowariancja, a C# na to nie pozwala. Jest jednak legalne w CLR i możesz przekonać C#, aby pozwolił ci to zrobić, jeśli chcesz.

+0

Ale wszystko jest ostatecznie system.object –

+0

Dzięki Gabe. Klucz kowariancji jest kluczem! –

1

to, co widzę, to tablica typów referencyjnych, które można przypisać do innej tablicy typów referencyjnych, podczas gdy tablica typów wartości nie może. Dla mnie sensowne są następujące typy referencji:

2

string i object. Oznacza to, że zmienna tych typów faktycznie przechowuje wskaźnik do innego miejsca w pamięci. int to typ wartości. Oznacza to, że dane są przechowywane bezpośrednio tam, gdzie deklarowana jest zmienna.

Oznacza to, że tablica typów odniesienia różni się zasadniczo od tablicy typów wartości. Tablica typów referencyjnych jest przechowywana jako tablica wskaźników. Ponieważ wskaźniki są tej samej wielkości, oznacza to, że ponowne interpretowanie wartości string[] jako wartości "object[]" oznacza dostosowanie kontroli typów podczas dostępu do elementów w tablicy (w przybliżeniu).

Jednak w postaci int[] wartości są przechowywane bezpośrednio w tablicy; wartości int są łączone razem. Żadne wskazówki nie są zaangażowane. Oznacza to, że ponowne interpretowanie int[] jako object[] wymaga umieszczenia każdej wartości przechowywanej w tablicy w obiekcie. Dlatego nie można tego zrobić za pomocą prostego rzutowania lub przypisania - jest to operacja O (n), która tworzy nową tablicę. Zamiast tego możesz użyć Array.Copy, która dotyczy wszystkich boksów.

0

Jeśli masz instalację Visual Studio, znajdziesz C# Language Speicifcation jako plik doc gdzieś pod VC#. Rozdział 12.5 dotyczy kowariancji i tam jest napisane

dla dowolnych dwóch typów wzorcowych A i B, jeśli niejawna odniesienia konwersji (§6.1.6) lub jawne konwersji odniesienia (§6.2.4) istnieje od A do B, wówczas taka sama konwersja referencyjna istnieje również z tablicą typu A [R] o typie do tablicy typu B [R], gdzie R jest dowolnym danym specyfikatorem pozycji (ale taki sam dla obu typów tablic). Ta relacja jest znana jako kowariancja macierzy.

To może nie odpowiedzieć na twoje pytanie, ale była to celowa decyzja w specyfikacji, aby zrobić to w ten sposób.

1

Dlaczego to nie działa, podano w wielu odpowiedziach tutaj, więc nie będę próbować kopiować i wklejać tego, co powiedzieli.

Jeśli Ty lub ktoś naprawdę chce to zrobić (konwersja int[] do object[]) z jakiegoś powodu, można użyć LINQ tak:

int[] ints = new int[] { 1, 2, 3 }; 
object[] obj_ints = (from i in ints select i).Cast<object>().ToArray(); 

;)

+0

Może być wiele obejść. Zobacz odpowiedź z "Gabe" poniżej –

+1

Dlaczego po prostu nie użyć 'Array.Copy'? http://msdn.microsoft.com/en-us/library/system.array.copy.aspx – Gabe

Powiązane problemy