Struktury są bardzo podobne do klas i każdą strukturę możemy jeżeli zechcemy zamienić w klasę lecz nie każdą klasę zamienimy w strukturę. Najczęściej jednak gdy piszemy aplikację często używamy samych klas i zapominamy o możliwości stworzenia struktury. W przypadku małych projektów nie ma to większego znaczenia, lecz gdy w projekcie będziemy chcieć poprawić wydajność wtedy, możemy pomyśleć nad przerobieniem pewnych klas i może wyodrębnieniem z nich struktur danych lub wręcz zmianie klasy na strukturę.
Struktur używa się przede wszystkim do zdefiniowania typów zawierających kilka związanych ze sobą danych, te dane są reprezentowane przez pola takiej struktury.

Podobnie jak klasa, struktura może zawierać: stałe, pola, metody, właściwości, indeksatory, konstruktory. Należy jednak pamiętać aby metody zawarte w strukturze nie wpływały znacząco na dane i nie zawierały logiki biznesowej do tego służą klasy.
Zapis w kodzie jest bardzo zbliżony do klasy, często strukturę określa się jako „lekka” bądź ograniczona klasa. Aby stworzyć strukturę używamy słowa kluczowego struct.
Zobaczmy przykład:

struct Rectangle {
       double height;
       double width;

       public double CalculateField() {
            return height * width;
        }

W strukturze nie jesteśmy w stanie stworzyć konstruktora bezparametrowego, jest on definiowany niejawnie i inicjuje wszystkie pola struktury wartościami domyślnymi. Nie mamy możliwości nadpisania tego konstruktora.
Należy pamiętać, że gdy tworzymy konstruktor z parametrami należy w nim określić wartości dla wszystkich pól w strukturze.

  public Rectangle(double width, double height) {
           this.width = width;
           this.height = height;
       }

  public Rectangle(double width) {
           this.width = width;
           this.height = 10; // bez przypisania wartości kompilator zgłosi błąd
       }

Problem ten możemy rozwiązać za pomocą łączenia konstruktorów w łańcuch. Jak już wiemy konstruktor domyślny w strukturze przypisuje każdemu z pól wartość domyślną w przypadku double będzie to 0 więc możemy po konstruktorze domyślnym za pomocą słowa kluczowego this.

public Rectangle(double width):this()  {
           this.width = width;           
       }

W ciele struktury nie możemy przypisywać wartości domyślnych w przeciwieństwie do klasy gdzie jest to dozwolone:

struct Rectangle {
       double height = 10;// kompilator zgłosi błąd
       double width;

       public double CalculateField() {
            return height * width;
        }
    }

Błąd zgłoszony przez kompilator po próbie przypisania wartości dla zmiennej w strukturze:

struct1

W odróżnieniu od klasy możemy stworzyć obiekt struktury bez użycia słowa kluczowego new.

Rectangle prostokatA;

W takim przypadku obiekt struktury nie tworzy się za pomocą konstruktora i nie ma zainicjonowanych pól przez co nie możemy z niego korzystać dopóki nie przypiszemy wartości do pól sami z poziomu kodu klienta za pomocą kropki. Ta operacja będzie możliwa wtedy gdy dla pól mamy ustawiony modyfikator dostępu na public.

struct Rectangle {
       public double height;
       public double width;

       public double CalculateField() {
            return height * width;
        }
    }

Napiszmy prosty program w którym w kodzie klienta przypiszemy wartości i obliczymy pole:
Gdy nie przypiszemy wartości do wszystkich pól struktury prostokatA kompilator zgłosi błąd Use of unsigned local variable ‘prostokatA’:

static void Main(string[] args) {
            Prostokat prostokatA;
            prostokatA.wysokosc = 10;
            Console.WriteLine("Pole prostokata wynosi: " + prostokatA.obliczPole());
    
 }


struct2
Rys. Błąd zgłoszony przez kompilator.

Poprawmy zatem kod:

static void Main(string[] args) {
Rectangle prostokatA;
       prostokatA.height = 10;
       prostokatA.width = 15;
            
Console.WriteLine("Pole prostokata wynosi: " + prostokatA.CalculateField());
//dzięki tej metodzie zobaczymy wynik na ekranie program się wykona po wciśnięciu dowolnego klawisza
            Console.ReadKey();        
        }

W tym przypadku otrzymamy wynik:

struct3

Co się stanie gdy użyjemy słowa kluczowego new przy tworzeniu obiektu struktury?

Rectangle prostokatA = new Rectangle ();

Wywoływany jest konstruktor, który przypisuje wartości domyśle do naszych pól. Przykład:

static void Main(string[] args) {
       Rectangle prostokatA = new Rectangle();           
       Console.WriteLine("Pole prostokata wynosi: " + prostokatA.CalculateField());
       Console.ReadKey();        
        }

W tym przypadku otrzymamy wynik :

struct4

Wartości w konstruktorze domyślnym dla naszych pól wynoszą 0. Skorzystajmy z napisanego przez nas wyżej konstruktora:

static void Main(string[] args) {
Rectangle prostokatA = new Rectangle(10,20);           
       Console.WriteLine("Pole prostokata wynosi: " + prostokatA.CalculateField());
Console.ReadKey();
        
        }

W tym przypadku otrzymamy wynik:

struct5

Struktura to zbiór różnych danych i kompilator domyślnie nie wie jak na nich wykonywać operacje dodawać, odejmować czy porównywać. Przez co struktury nie mają wbudowanych zachowań dla operatorów.
Obiekty struktury dodawane są na stosie, ponieważ jest to typ wartościowy. Ma to pewne zalety, tworzenie i usuwanie obiektów na stosie trwa krócej niż na stercie. Więc w pewnych przypadkach może nam to poprawić wydajność aplikacji. Pamiętajmy jednak o tym, że gdy przekazujemy obiekty naszej struktury jako parametr dla funkcji, za każdym razem je kopiujemy. Efektywniej wtedy jest skorzystać z klasy ponieważ, jej obiekty przekazywane są przez referencje. Struktur powinniśmy używać jeżeli obiekty stworzonego przez nas typu będą niewielkie i używane w małych zakresach. Gdy korzystamy z struktur nie możemy użyć mechanizmu dziedziczenia.