Interfejs to zbiór powiązanych ze sobą logicznie deklaracji funkcji. W skład interfejsu mogą wchodzić:

– funkcje
– własności
– eventy
– indeksatory

Wszystkie elementy interfejsu są automatycznie publiczne. Nie zapisujemy w nim implementacji metod a jedynie deklaracje. Określamy typ zwracany przez funkcję oraz ilość i rodzaj parametrów wejściowych. Zobaczmy przykład:

public interface IWypiszDaneFirmy {
        string NazwaFirmy { get;}
        string WypiszAdres();
        string WypiszPelneDane();
        string DaneOddzialu(string nazwaOddzialu);    
    }

Nasz przykładowy interfejs składa się z własności typu string NazwaFirmy oraz z trzech funkcji. Są one również typu string.
Interfejs jest pewnego rodzaju kontraktem. Klasa lub struktura która go implementuje musi wprowadzić implementację dla elementów interfejsu.

Aby szybko rozróżnić interfejs od innego elementu przyjęto, że jego nazwę rozpoczynamy od dużej litery I. Jest to często krytykowana konwencja. Jednak powszechnie stosowana przez programistów C#. Przez co polecam ją przestrzegać.

Implementacja interfejsu przypomina dziedziczenie temu często możemy usłyszeć błędne stwierdzenie: „Klasa może dziedziczyć tylko po jednej klasie, natomiast po wielu interfejsach”. Poprawnie jest: „Klasa może dziedziczyć tylko po jednej klasie, natomiast może implementować wiele interfejsów”.
Zobaczmy przykład implementacji naszego interfejsu:

public class FirmaA : IWypiszDaneFirmy {
    }

Nasza klasa FirmaA implementuje nasz interfejs o nazwie IWypiszDaneFirmy jednak nie wprowadza implementacji dla jego elementów. Visual studio zgłosi błędy:


Rys. Błędy zgłoszone przez kompilator.

Możemy przyśpieszyć nieco kodowanie kliknijmy ikonę żarówki obok naszej kasy następnie wybierzmy Implement interface.

Rys. Dodanie części kodu przez Visual Studio 2015

Dostaliśmy następujący kod:

    public class FirmaA : IWypiszDaneFirmy {
        public string NazwaFirmy {
            get {
                throw new NotImplementedException();
            }
        }

        public string DaneOddzialu(string nazwaOddzialu) {
            throw new NotImplementedException();
        }

        public string WypiszAdres() {
            throw new NotImplementedException();
        }

        public string WypiszPelneDane() {
            throw new NotImplementedException();
        }
    }

Kompilator już nie zgłasza błędów jednak nasza klasa w każdej metodzie zgłasza wyjątek NotImplementedException. Zmieńmy zatem nasz kod:

public class FirmaA : IWypiszDaneFirmy {
        private string nazwaFirmy = "Pisz-Kod";
        private string adres = String.Format("Ul. Rynek 12 {0} Kraków 23-235", Environment.NewLine);

        public string NazwaFirmy {
            get {
               return nazwaFirmy;
            }
        }

        public string DaneOddzialu(string nazwaOddzialu) {
            switch (nazwaOddzialu) {
                case "A":
                    return String.Format("{0}{1} Odział A {1} Ul. Miodna 33 {1} Warszawa 33-343", nazwaFirmy,Environment.NewLine);
                case "B":
                    return  String.Format("{0}{1} Odział B {1} Ul. Zabawa 55 {1} Poznań 41-121", nazwaFirmy, Environment.NewLine);
                default:
                    return "Błędna nazwa oddziału";

            }
        }

        public string WypiszAdres() {
            return adres;
        }

        public string WypiszPelneDane() {
            return String.Format("{0}{1} {2}",nazwaFirmy,Environment.NewLine,adres);
        }
    }

Nasza klasa FirmaA zawiera własną implementację interfejsu IWypiszDaneFirmy.

Dzięki zastosowaniu interfejsu oddzielamy abstrakcję od implementacyjnych szczegółów. Przez co w klasie używamy interfejs a jego implementację możemy wstrzyknąć np. poprzez konstruktor taki zabieg nazywamy dependency injection. Z abstrakcją, którą otrzymujemy za pomocą interfejsu jest związana także zasada dependency inverson (jedna z zasad SOLID).