Interfejsy działają w pewnym stopni jak klasy abstrakcyjne w C++, jednakże interfejsy ograniczają się do deklaracji metod i właściwości, które klasa dziedzicząca po takim interfejsie musi obsłużyć. Oto prosty przykład interfejsu:
Teraz, jeżeli w kodzie programu utworzę klasę dziedziczącą po interfejsie IPoint2D, to taka klasa musi obsłużyć wszystkie metody i właściwości, zadeklarowane w tymże interfejsie. Poniżej zamieszczam przykład implementacji dwóch interfejsów i dwóch klas po nich dziedziczących:
interface IPoint2D
{
double Length { get; set; }
string ToString();
double X { get; set; }
double Y { get; set; }
}
interface IPoint3D : IPoint2D
{
new double Length { get; set; }
new string ToString();
double Z { get; set; }
}
// ########################### DEKLARACJA KLASY POINT@D #############################
public class Point2D : IPoint2D
{
// ####################### CHRONIONE POLA KLASY #################################
protected double x;
protected double y;
// ####################### KONSTRUKTORY KLASY ###################################
public Point2D() // konstruktor zerujący
{
x = y = 0;
}
public Point2D(double x, double y) // konstruktor ustawiajacy
{
this.x = x;
this.y = y;
}
public Point2D(Point2D p) // kosntruktor kopiujący
{
this.x = p.x;
this.y = p.y;
}
// ######## WŁAŚCIWOŚCI KLASY ##########
public double Y
{
get { return y; }
set { y = value; }
}
public double X
{
get { return x; }
set { x = value; }
}
public double Length
{
get { return Math.Sqrt(x * x + y * y); } // pobieranie długości wektora
set // ustawianie długości wektora
{
if (x != 0 || y != 0) // gdy wektor nie jest zerowy to
{
double k = value / Length;
x *= k; // skalowanie składowej x wektora
y *= k; // skalowanie składowej y wektora
}
}
}
// ########### METODY #############
public override string ToString() // przeładowana metoda ToString klasy bazowej Object
{
return "Point2D x = " + x + "; y = " + y;
}
}
// ############################## DEKLARACJA KLASY POINT3D #################################
public class Point3D : Point2D, IPoint3D // dziedziczy po Point2D
{
// ########################## CHRONIONE POLA KLASY #####################################
protected double z;
// ########################## KONSTRUKTORY KLASY #######################################
public Point3D()
: base() // wywołanie bezparametrowego kosntruktora klasy bazowej
{
z = 0;
}
public Point3D(double x, double y, double z)
: base(x, y) // wywołanie ustawiającego konstruktora klasy bazowej
{
this.z = z;
}
public Point3D(Point3D p) // konstruktor kopiujący
: base(p) // wywołanie kontruktora kopiującego klasy bazowej
{
z = p.z;
}
// ################# WŁAŚCIWOŚCI ###############
public double Z
{
get { return z; }
set { z = value; }
}
public new double Length // przysłanianie właściwości z klasy bazowej (oznacza to słowo kluczowe new)
{
get { return Math.Sqrt(x * x + y * y + z * z); } // pobieranie długości wektora
set // ustawianie długości wektora
{
if (x != 0 || y != 0 || z != 0)
{
double k = value / Length;
X *= k;
Y *= k;
z *= k;
}
}
}
// ################# METODY ################
public new string ToString() // przysłanianie metody z klasy bazowej (oznacza to słowo kluczowe new)
{
return "Point3D x = " + X + "; y = " + Y + "; z = " + z;
}
}
Przykład zastosowanie interfejsów do przechowywania różnych zmiennych w jednej tablicy:
namespace Klasy
{
class Program
{
static void Main(string[] args)
{
IPoint2D[] t = new IPoint2D[2];
t[0] = new Point2D(100, 200);
t[1] = new Point3D(20, 30, 40);
foreach (IPoint2D p in t)
{
if (p is Point3D)
{
Console.WriteLine(((Point3D)p).ToString());
}
else
{
Console.WriteLine(p.ToString());
}
}
Console.ReadLine();
}
}
}
Wynik działania powyższego kodu:
Point2D x = 100; y = 200
Point3D x = 20; y = 30; z = 40
Nic nie stoi na przeszkodzie, aby utworzyć metodę klasy, która będzie przyjmowała jako argument dany interfejs. W takim przypadku każdy obiekt klasy, która dziedziczy po owym interfejsie będzie mógł zostać przekazany jako parametr takiej metody.