• C# i .NET
  • Jak kopiować tablicę w C#? Praktyczny wybór metod

Jak kopiować tablicę w C#? Praktyczny wybór metod

Bruno Krawczyk 11 sierpnia 2026
Tablice #6. Jak zrobić c# copy array? Oto rozwiązanie.

Spis treści

Gdy w kodzie potrzebuję utworzyć kopię tablicy w C#, najpierw sprawdzam, czy chodzi o nową tablicę, czy tylko o drugi dostęp do tych samych danych. To rozróżnienie decyduje o wyborze między przypisaniem referencji, Array.Copy, Clone, LINQ i nowszą składnią kolekcji. Poniżej pokazuję praktyczne przykłady, różnice między kopiowaniem płytkim i głębokim oraz typowe pułapki.

Najszybszy wybór metody zależy od rodzaju danych

  • Array.Copy sprawdza się przy klasycznym kopiowaniu całej tablicy lub jej fragmentu.
  • Clone() tworzy kopię płytką i jest wygodny przy tablicach wartości.
  • ToArray() dobrze pasuje do potoków LINQ i zwraca nową tablicę.
  • [.. tablica] to czytelna składnia dostępna od C# 12.
  • Przy elementach będących obiektami sama kopia tablicy nie wystarczy do uzyskania kopii głębokiej.

Kod C# kopiuje tablicę, tworząc wykresy i wizualizacje danych.

Najpierw rozdziel referencję od kopii danych

Najczęstszy błąd wygląda niewinnie i polega na zwykłym przypisaniu tablicy do drugiej zmiennej:

int[] oryginalna = { 1, 2, 3 };
int[] kopia = oryginalna;

kopia[0] = 99;

Console.WriteLine(oryginalna[0]); // 99

Obie zmienne wskazują wtedy na ten sam obiekt w pamięci. Nie powstała żadna kopia, więc zmiana wykonana przez `kopia` jest widoczna także przez `oryginalna`. W praktyce taki kod ma sens tylko wtedy, gdy świadomie chcemy udostępnić tę samą tablicę.

Prawdziwa kopia tworzy nową instancję tablicy i przepisuje do niej elementy. Dla typów wartości, takich jak `int`, `double` czy `DateTime`, efekt jest zwykle intuicyjny. Przy typach referencyjnych kopiowane są jednak często same referencje do obiektów, a nie obiekty znajdujące się pod tymi referencjami.

Najpraktyczniejsze sposoby kopiowania tablicy

Array.Copy przy pełnej tablicy i wybranym zakresie

Najbardziej uniwersalnym rozwiązaniem jest Array.Copy. Najpierw tworzymy tablicę docelową, a potem przepisujemy do niej elementy:

int[] oryginalna = { 10, 20, 30 };
int[] kopia = new int[oryginalna.Length];

Array.Copy(oryginalna, kopia, oryginalna.Length);

kopia[0] = 99;

Console.WriteLine(oryginalna[0]); // 10
Console.WriteLine(kopia[0]);      // 99

Ta metoda jest szczególnie przydatna, gdy kopiuję tylko fragment danych. Parametry określają tablicę źródłową, indeks początkowy, tablicę docelową, indeks docelowy i liczbę elementów:

int[] oryginalna = { 10, 20, 30, 40, 50 };
int[] fragment = new int[3];

Array.Copy(oryginalna, 1, fragment, 0, 3);

// fragment zawiera: 20, 30, 40

Przy kopiowaniu fragmentów trzeba pilnować długości tablicy docelowej oraz zakresu indeksów. Jeśli zabraknie miejsca albo któryś indeks wyjdzie poza tablicę, otrzymamy ArgumentException lub ArgumentOutOfRangeException.

Clone jako krótka kopia płytka

Metoda `Clone()` jest bardzo zwięzła i tworzy nową tablicę o takim samym rozmiarze. Ponieważ zwraca typ `object`, w kodzie często pojawia się rzutowanie:

int[] oryginalna = { 1, 2, 3 };
int[] kopia = (int[])oryginalna.Clone();

kopia[1] = 200;

Console.WriteLine(oryginalna[1]); // 2

W przypadku tablicy liczb otrzymujemy niezależne wartości, ale technicznie jest to kopiowanie płytkie. Dla tablicy obiektów nowa jest sama tablica, natomiast jej elementy nadal mogą wskazywać na te same instancje.

ToArray w kodzie korzystającym z LINQ

Jeżeli tablica jest częścią operacji LINQ, `ToArray()` zwykle czyta się najlepiej:

using System.Linq;

int[] oryginalna = { 1, 2, 3, 4 };
int[] kopia = oryginalna.ToArray();

Metoda tworzy nową tablicę i kopiuje do niej elementy. Nie używam jej jednak bez powodu w prostym, gorącym fragmencie kodu, ponieważ wymaga LINQ i może zaciemnić prostą operację. Jej mocną stroną jest naturalne zakończenie potoku przekształceń:

int[] liczby = { 1, 2, 3, 4, 5 };

int[] kopia = liczby
    .Where(x => x % 2 == 1)
    .Select(x => x * 10)
    .ToArray();

// kopia zawiera: 10, 30, 50

Składnia spread dostępna od C# 12

W nowszym C# tablicę można skopiować za pomocą składni kolekcji:

int[] oryginalna = { 1, 2, 3 };
int[] kopia = [.. oryginalna];

To rozwiązanie jest krótkie i dobrze pokazuje intencję. Używam go chętnie w nowym kodzie, o ile projekt korzysta z C# 12 lub nowszego. W starszym projekcie lepiej wybrać `Array.Copy`, `Clone()` albo `ToArray()`, zależnie od kontekstu.

Przeczytaj również: Enum w C# - jak używać go bezpiecznie?

CopyTo i Span przy świadomym kopiowaniu pamięci

Tablica ma również metodę `CopyTo`, ale wymaga wcześniej utworzonej tablicy docelowej:

int[] oryginalna = { 1, 2, 3 };
int[] kopia = new int[oryginalna.Length];

oryginalna.CopyTo(kopia, 0);

W kodzie nastawionym na pracę z buforami można użyć `Span`:

int[] oryginalna = { 1, 2, 3 };
int[] kopia = new int[oryginalna.Length];

oryginalna.AsSpan().CopyTo(kopia);

Nie traktowałbym jednak `Span` jako automatycznie szybszego rozwiązania w każdej sytuacji. Daje wygodny dostęp do fragmentów pamięci i dobrze pasuje do kodu niskopoziomowego, ale przy zwykłej kopii tablicy czytelność jest ważniejsza niż mikrooptymalizacja.

Którą metodę wybrać w konkretnym przypadku

Różnice między metodami są niewielkie przy małych tablicach, ale ich znaczenie rośnie, gdy kod jest często wykonywany albo kopiuje duże bufory. Najlepiej dobrać rozwiązanie do intencji, a nie do samej długości zapisu.

Metoda Najlepsze zastosowanie Najważniejsze ograniczenie
Array.Copy Pełna tablica lub wybrany fragment Trzeba utworzyć tablicę docelową
Clone() Krótka kopia tablicy tego samego typu Zwraca object i jest płytka
ToArray() Końcowy etap operacji LINQ Wymaga LINQ i tworzy nową tablicę
[.. źródło] Nowoczesny, czytelny kod w C# 12+ Nie pasuje do starszych wersji języka
Span.CopyTo Bufory, zakresy i kod wydajnościowy Może być przesadą przy prostych operacjach

Moja praktyczna reguła jest prosta. Do zwykłej kopii tablicy wartości wybieram Array.Copy albo składnię spread. Gdy pracuję w LINQ, korzystam z `ToArray()`, a gdy kopiuję konkretny zakres, sięgam po przeciążenie `Array.Copy` z indeksami.

Kopia płytka i głęboka przy tablicach obiektów

Sprawa komplikuje się, gdy tablica przechowuje obiekty klas:

class Produkt
{
    public string Nazwa { get; set; } = "";
}

Produkt[] oryginalna =
{
    new Produkt { Nazwa = "Klawiatura" }
};

Produkt[] kopia = (Produkt[])oryginalna.Clone();

kopia[0].Nazwa = "Mysz";

Console.WriteLine(oryginalna[0].Nazwa); // Mysz

Tablice są różne, ale oba elementy wskazują na ten sam obiekt Produkt. `Clone()`, `Array.Copy`, `ToArray()` i składnia spread kopiują tutaj referencję. Nie tworzą automatycznie nowych produktów.

Kopia głęboka wymaga jawnego skopiowania każdego elementu oraz jego zagnieżdżonych danych. Najczęściej robię to przez konstruktor kopiujący, metodę fabrykującą albo mapowanie:

Produkt[] kopia = oryginalna
    .Select(produkt => new Produkt
    {
        Nazwa = produkt.Nazwa
    })
    .ToArray();

W tym przykładzie `string` nie wymaga dodatkowego kopiowania, ponieważ jest typem niemutowalnym. Gdy `Produkt` ma na przykład listę kategorii albo obiekt adresu, trzeba utworzyć również nowe instancje tych zagnieżdżonych elementów. Samo `Select` bez tworzenia nowych obiektów nie rozwiąże problemu.

Tablice wielowymiarowe i zagnieżdżone

Tablica dwuwymiarowa z typami wartości może zostać skopiowana tak samo jak tablica jednowymiarowa:

int[,] macierz =
{
    { 1, 2 },
    { 3, 4 }
};

int[,] kopia = (int[,])macierz.Clone();

kopia[0, 0] = 99;

Console.WriteLine(macierz[0, 0]); // 1

Więcej uwagi wymaga tablica postrzępiona, czyli tablica tablic. Przy takim kodzie:

int[][] oryginalna =
{
    new[] { 1, 2 },
    new[] { 3, 4 }
};

int[][] kopia = (int[][])oryginalna.Clone();

skopiowana zostaje tylko tablica zewnętrzna. Wewnętrzne wiersze nadal są współdzielone. Aby uzyskać niezależną kopię, trzeba skopiować każdy wiersz osobno:

int[][] kopia = oryginalna
    .Select(wiersz => wiersz.ToArray())
    .ToArray();

kopia[0][0] = 99;

Console.WriteLine(oryginalna[0][0]); // 1

To jeden z przypadków, w których krótki `Clone()` może dać pozornie poprawny, ale błędny rezultat. Przy strukturach zagnieżdżonych zawsze sprawdzam, na którym poziomie kończy się kopiowanie.

Typowe błędy, których łatwo uniknąć

Poza przypisaniem referencji często spotykam nieprawidłowe użycie `Array.Copy`. Tablica docelowa musi mieć wystarczający rozmiar, a liczba kopiowanych elementów nie może przekraczać dostępnego zakresu:

int[] źródło = { 1, 2, 3 };
int[] cel = new int[2];

Array.Copy(źródło, cel, źródło.Length); // błąd

Jeśli potrzebujemy tylko dwóch pierwszych elementów, liczba kopiowanych wartości musi wynosić `2`. Jeśli potrzebujemy całej tablicy, rozmiar celu powinien pochodzić z `źródło.Length`, a nie z ręcznie wpisanej liczby.

Drugie nieporozumienie dotyczy wydajności. Nie zakładam z góry, że `Buffer.BlockCopy` będzie najlepszy. Jest przeznaczony głównie do kopiowania danych z tablic typów prostych na poziomie bajtów i łatwo użyć go niewłaściwie. W typowym kodzie biznesowym Array.Copy jest bezpieczniejszym i czytelniejszym wyborem.

Trzeba też pamiętać o `null`. Wywołanie `oryginalna.Clone()` albo `oryginalna.ToArray()` dla pustej referencji zakończy się wyjątkiem. Jeżeli brak tablicy jest poprawnym stanem, obsłuż go jawnie zamiast maskować problem przypadkowym pustym wynikiem.

Najlepsza kopia to czasem brak kopiowania

Jeżeli funkcja ma tylko odczytać dane, tworzenie nowej tablicy może być niepotrzebnym kosztem. W takim przypadku rozważam przekazanie `ReadOnlySpan`, `IReadOnlyList` albo zwykłej tablicy bez modyfikowania jej zawartości.

Kopię tworzę wtedy, gdy potrzebuję niezależnego cyklu życia danych, ochrony przed zmianami albo osobnego bufora roboczego. Dla typowej tablicy wartości wystarczy Array.Copy, Clone lub składnia [..]; przy obiektach i strukturach zagnieżdżonych trzeba już świadomie zaprojektować kopiowanie głębokie. To właśnie ta decyzja, a nie sama długość kodu, najczęściej decyduje o poprawności rozwiązania.

FAQ - Najczęstsze pytania

Przypisanie w rodzaju int[] kopia = oryginalna; nie tworzy nowej tablicy. Obie zmienne wskazują ten sam obiekt, więc zmiana przez kopia jest widoczna w oryginalna. Niezależną tablicę utworzysz między innymi przez Array.Copy, Clone(), ToArray() lub składnię [.. oryginalna].

Array.Copy pasuje do pełnej tablicy i kopiowania wybranego zakresu. Clone() jest krótkie, ale zwraca object, ToArray() dobrze kończy potok LINQ, a [.. źródło] jest czytelne w C# 12 i nowszym.

Nie. Metody te tworzą nową tablicę, ale elementy będące obiektami nadal mogą wskazywać te same instancje. Kopię głęboką trzeba wykonać jawnie, tworząc nowy obiekt dla każdego elementu oraz nowe instancje jego zagnieżdżonych danych.

Clone() kopiuje tylko zewnętrzną tablicę, więc wewnętrzne wiersze pozostają współdzielone. Aby je rozdzielić, skopiuj każdy wiersz osobno, na przykład przez oryginalna.Select(wiersz => wiersz.ToArray()).ToArray().

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi

tablice
linq
kopiowanie
array.copy
span
Autor Bruno Krawczyk
Bruno Krawczyk
Mam na imię Bruno i od 8 lat zgłębiam tajniki programowania w ekosystemie .NET, chmury Azure oraz sztucznej inteligencji. Moja przygoda z technologią zaczęła się od ciekawości, jak złożone systemy mogą ułatwiać codzienne życie i rozwiązywać realne problemy. Dziś moją misją jest dzielenie się tą wiedzą, starając się przybliżyć nawet najbardziej skomplikowane zagadnienia w sposób zrozumiały i przystępny dla każdego. W moich artykułach na kursdotnet.pl skupiam się na praktycznych aspektach, analizuję najnowsze trendy i weryfikuję informacje, aby dostarczyć Wam treści, które są nie tylko dokładne i aktualne, ale przede wszystkim użyteczne w Waszej własnej ścieżce rozwoju technologicznego.

Udostępnij artykuł

Napisz komentarz