Archiwa blogu

Jawna implementacja interfejsów jako rozwiązanie konfliktu nazw

2012-08-25

Implementując w danej klasie kilka interfejsów możemy spotkać się z sytuacją, w której różne interfejsy będą posiadały składowe o tych samych nazwach:

interface IFirstInterface
{
    void SomeMethod();
}

interface ISecondInterface
{
    void SomeMethod();
}

class SomeClass : IFirstInterface, ISecondInterface
{
    public void SomeMethod()
    {
        Console.WriteLine("Call some method...");
    }
}

Powyższy kod prezentuje klasę implementującą dwa interfejsy zawierające metody o tej samej nazwie. W klasie metoda zaimplementowana jest raz i działa bez względu na to, za pośrednictwem którego interfejsu zostanie wywołana:

static void Main(string[] args)
{
    SomeClass someClass = new SomeClass();

    IFirstInterface iFirst = someClass;
    ISecondInterface iSecond = someClass;

    iFirst.SomeMethod(); //Call some method...
    iSecond.SomeMethod(); //Call some method...

    Console.ReadLine();
}

A co jeżeli metoda pierwszego interfejsu ma inne przeznaczenie niż metoda drugiego i ich kod powinien być różny? Rozwiązaniem tego problemu jest jawna implementacja interfejsów:

class SomeClass : IFirstInterface, ISecondInterface
{
    void IFirstInterface.SomeMethod()
    {
        Console.WriteLine("Call some method from first interface...");
    }

    void ISecondInterface.SomeMethod()
    {
        Console.WriteLine("Call some method from second interface...");
    }
}

W przypadku jawnej implementacji nazwę składowej poprzedzamy nazwą danego interfejsu. Drugą ważną rzeczą jest pominięcie modyfikatora dostępu, ponieważ jawnie implementowana składowa automatycznie otrzymuje modyfikator private i nie można tego zmienić. Fakt ten oznacza, że taka składowa nie będzie dostępna przy odwołaniu z poziomu obiektu a jedynie z poziomu danego interfejsu. Oczywiście w powyższym kodzie można na przykład jawnie zaimplementować jedynie drugi interfejs, dzięki temu pierwsza wersja metody będzie dostępna z poziomu obiektu oraz interfejsu IFirstInterface, natomiast druga wersja jedynie z poziomu interfejsu ISecondInterface. Oto działanie jawnej implementacji:

static void Main(string[] args)
{
    SomeClass someClass = new SomeClass();

    IFirstInterface iFirst = someClass;
    ISecondInterface iSecond = someClass;

    iFirst.SomeMethod(); //Call some method from first interface...
    iSecond.SomeMethod(); //Call some method from second interface...

    Console.ReadLine();
}

Jak widać, wywołując metodę za pośrednictwem wybranego interfejsu, obiekt jest w stanie określić jej odpowiednią wersję. Oprócz rozwiązywania konfliktów nazw, jawną implementację interfejsów można wykorzystać także do ukrywania pewnych elementów klasy przy dostępie z poziomu obiektu. Elementy te będą jednocześnie dostępne przy odwołaniu poprzez interfejs.
Na zakończenie warto dodać, że powinno unikać się sytuacji, w których składowe o tych samych nazwach różnią się przeznaczeniem lub sposobem działania. Jeżeli jednak nie mamy na to wpływu (na przykład korzystamy z bibliotek zewnętrznych) jawna implementacja interfejsów pozwoli nam na rozwiązanie konfliktu nazw.