Przyspieszony kurs C# pod Unity3d dla leniwych i opornych. Dzisiaj: Interfejsy i metody wirtualne

Poprzednie lekcje – Spis Treści

W poprzednich częściach poznaliśmy wiele różnych przydatnych narzędzi, które się ze sobą łączyły i poniekąd wynikały z siebie. Jedak zostały wg. mnie trzy istotne zagadnienia, których do tej pory nie ruszyliśmy, a mogą być dość przydatne.

Interfejsy

Interfejs jest śmiesznym słowem, które każdy rozumie podświadomie, ale nikt nie umie go w prosty sposób opisać słowami. Więc czym jest interfejs? Przypomina on nieco klasy abstrakcyjne. Pozwala nam wymusić na danej klasie posiadanie pewnego zestawu metod. Z taką różnicą, że tutaj definiujemy jedynie nazwy metod. Nie możemy dodać nic po za tym.

Zaczniemy od prostego przykładu:

using UnityEngine;
using System.Collections;

interface myInterface
{
    void writeTxt(string txt);
    int randomInt();
}


public class Lekcja_07 : MonoBehaviour, myInterface
{

}

Widać tutaj jak prosto buduje się interfejs. Zaczynamy słowem kluczowym interface. Potem podajemy jego nazwę i następnie nasze metody, gdzie podajemy tylko typ zwracany, nazwę i parametry. Z takim interfejsem nie możemy zrobić nic, po za wykorzystaniem go w dziedziczeniu.

Tutaj ważna uwaga. Klasa może dziedziczyć tylko po jednej innej klasie. Ale może dziedziczyć po wielu interfejsach. Również może dziedziczyć jednocześnie po klasie i interfejsie. Powyższy kod jednak zwróci błąd:
[stextbox id=”info” defcaption=”true”]Error CS0535 ‘Lekcja_07’ does not implement interface member ‘myInterface.writeTxt(string)’ KursCSharp.CSharp F:\Unity\QuickTip\KursCSharp\Assets\Lekcja_07.cs 11 Active

Error CS0535 ‘Lekcja_07’ does not implement interface member ‘myInterface.randomInt()’ KursCSharp.CSharp F:\Unity\QuickTip\KursCSharp\Assets\Lekcja_07.cs 11 Active[/stextbox]
Znów dobrze nam znany błąd. Czyli klasa nie posiada funkcji określonych przez interfejs. Naprawimy to, dodając te dwie funkcję:

Przykład 1

using UnityEngine;
using System.Collections;

interface myInterface
{
    void writeTxt(string txt);
    int randomInt();
}


public class Lekcja_07 : MonoBehaviour, myInterface
{
    public void writeTxt(string txt) {

    }

    public int randomInt() {
        return 0;
    }
}

Kolejną ważną uwagą jest fakt, że metody wymuszone przez interfejs muszą być publiczne! Dodajmy sobie jeszcze dowód na to, że możemy dziedziczyć po wielu interfejsach na raz:

Przykład 2

using UnityEngine;
using System.Collections;

interface myInterface
{
    void writeTxt(string txt);
}

interface myInterface2
{
    int randomInt();
}


public class Lekcja_07 : MonoBehaviour, myInterface, myInterface2
{
    public void writeTxt(string txt) {

    }

    public int randomInt() {
        return 0;
    }
}

Gdzie to wykorzystać? Jeśli mamy w grze kilka różnych obiektów, które muszą mieć funkcję pozwalające na modyfikowanie ich punktów życia, możemy sobie przygotować taki interfejs. Dzięki temu, kompilator nie pozwoli nam zapomnieć o tych funkcjach.

Struktura

Struktura to w sumie… coś w rodzaju definicji wyglądu obiektu, albo zbiór danych. Możemy tam trzymać zmienne (ale nie możemy ustalać ich wartości). Możemy również mieć zdefiniowane funkcję.

Przykład 3

using UnityEngine;
using System.Collections;

struct myStructure
{
    public int x;
    public float y;
    private string z;

    public void writeSth() {
        Debug.Log(z);
    }
}

public class Lekcja_07 : MonoBehaviour
{
    void Start() {
        myStructure test = new myStructure() { x = 3, y = 2.3f };

        Debug.Log(test.x + " " + test.y);
        test.writeSth();

        test.x = 4;
        test.y = 3.5f;

        Debug.Log(test.x + " " + test.y);
        test.writeSth();
    }
}

Strukturę definiujemy przez podanie słowa kluczowego struct, a następnie nazwy. W nawiasach klamrowych definiujemy elementy. Czym się różnic od klas? Przede wszystkim struktury nie obejmują dziedziczenia. Są bardziej przeznaczone do przechowywania lekkich, ale bardziej skomplikowanych zbiorów danych.

Gdzie w Unity można sprytnie wykorzystać strukturę? Zauważmy, że przydatna funkcja SendMessage, pozwala przesłać tylko jeden parametr. Jedną zmienną. Ale czasami potrzebujemy przesłać więcej danych. Można wtedy utworzyć strukturę ze wszystkimi potrzebnymi polami i ją przesyłać.

Enumeracje

Ostatnia ciekawa rzecz to enumeratory, czyli typy wyliczeniowe. Mają za zadanie 2 rzeczy. Po pierwsze uproszczenie rozumienia kodu. Po drugie przyspieszenie działania. Czym właściwie są? Listą kolejno ponumerowanych słów:

Przykład 4

using UnityEngine;
using System.Collections;

public class Lekcja_07 : MonoBehaviour
{
    enum Days : int { Pon, Wt, Sr, Czw, Pt, Sob, Nd };

    void Start() {
        Debug.Log("Pon: " + (int)Days.Pon);
        Debug.Log("Sr: " + (int)Days.Sr);
    }
}

Taki kod wypisze nam Pon jako 0 (liczymy od zera) i Sr jako 2. W przypadku dni tygodnia może być to bez sensu, ale zobaczmy na przykład z logerem błędów:

Przykład 5

using UnityEngine;
using System.Collections;

public class Lekcja_07 : MonoBehaviour
{
    enum Logger : int { INFO, NOTICE, WARNING, ERROR };

    void Start() {
        int x = 2;
        switch(x) {
            case (int)Logger.INFO:
                Debug.Log("To tylko info... uff...");
            break;
            case (int)Logger.NOTICE:
                Debug.Log("Notice? Mozna to poprawic");
            break;
            case (int)Logger.WARNING:
                Debug.Log("Lepiej to poprawic");
            break;
            case (int)Logger.ERROR:
                Debug.Log("Lolaboga nie dziala!");
            break;
        }
    }
}

O ile taki kod jest czytelniejszy, niż gdybyśmy wstawili jako warunki zwykłe cyferki? Mało tego, możemy modyfikować domyślne wartości. Tzn. Standardowo pierwszy wpis będzie równy 0, kolejny to 1, następny 2 itd. Ale możemy wymusić inne wartości, np.:

enum Logger : int { INFO = 1, NOTICE = 3, WARNING = 5, ERROR = 7 };

Dzięki takiemu zapisowi, kod jest czytelniejszy, my nie musimy pamiętać cyferek (wystarczy w edytorze podać “Logger.”, a IDE samo nam podpowie jakie mamy opcję.

Podsumowanie kursu

I to tyle! Udało nam się razem dotrwać do końca kursu. Tak jak wspominałem na samym początku, nie jest to kurs kompletny od zera do programisty C#. Ale jeśli jara Cię Unity i chcesz rozumieć podstawowy kod i potrafić zrobić coś ekstra, to ta wiedza powinna Ci wystarczyć na początek. Idź i baw się programowaniem, poszerzaj swoją wiedzę i przyj na przód.

Programowanie to ciągła nauka. Język się zmienia, a najlepszą nauką jest doświadczenie i samodzielne rozwiązywanie swoich problemów.

Jeżeli pomimo wszystko masz dalszy głód wiedzy, to musisz sięgnąć po bardziej rozbudowane poradniki, albo napisz w komentarzu o czym z dziedziny C# chciałbyś się więcej dowiedzieć, a może zrealizuje kolejne poradniki, poświęcone samemu językowi.

Klasy abstrakcyjne i static <- Poprzednia Lekcja