2009-09-09 12 views
6

Rozumiem, jak słowo kluczowe "nowe" może ukryć metody w klasie pochodnej. Jakie są jednak implikacje dla klas implementujących interfejsy wykorzystujące słowo kluczowe?Czy możesz użyć słowa kluczowego C#, aby rozwinąć właściwości w interfejsie?

Rozważmy ten przykład, w którym decyduję się rozwinąć interfejs, zmieniając jego właściwości w odczyt/zapis.

public interface IReadOnly { 

    string Id { 
     get; 
    } 
} 

public interface ICanReadAndWrite : IReadOnly { 

    new string Id { 
     get; 
     set; 
    } 
} 

Wtedy jesteś w stanie robić takie rzeczy:

public IReadOnly SomeMethod() { 
    // return an instance of ICanReadAndWrite 
} 

Czy to zły projekt? Czy spowoduje to problemy dla moich klas, które implementują ICanReadAndWrite?

Edit: Oto zmyślony przykład dlaczego może chcę zrobić coś takiego:

że mam klasy fabrycznej, która zwraca IShoppingCartItemReadWrite. Mogę wtedy mieć warstwę usługową, która manipuluje cenami, zmienia rzeczy itp. Następnie mogę przekazać te obiekty jako IShoppingCartItemReadOnly do jakiejś warstwy prezentacji, która ich nie zmieni. (Tak, wiem, że to technicznie możliwezmiana them-- jest to kwestia projektu, nie bezpieczeństwa, itd.)

+0

Twój konkretny przykład nie ma większego sensu. Jeśli potrafisz czytać i pisać, to nie jest to tylko czytanie. –

+0

Znacznik IShoppingCartItemReadWrite można zmienić, ale jeśli mam odwołanie do IShoppingCartItemReadOnly, to jest on tylko do odczytu, chyba że go rzucisz na coś innego. – user10789

+0

Pamiętaj, że nie potrzebujesz w tym miejscu słowa kluczowego "new". –

Odpowiedz

19

To nie jest szczególnie zły pomysł. Trzeba mieć świadomość, że implementor może (jeżeli niejawnie implementuje interfejs, a następnie pojedynczy odczyt/zapis właściwości mogłyby zaspokoić oba interfejsy) zapewniają dwa różne implementacje:

class Test : ICanReadAndWrite { 
    public string Id { 
     get { return "100"; } 
     set { } 
    } 
    string IReadOnly.Id { 
     get { return "10"; } 
    } 
} 

Test t = new Test(); 
Console.WriteLine(t.Id); // prints 100 
Console.WriteLine(((IReadOnly)t).Id); // prints 10 

Nawiasem mówiąc, w ogóle, new modyfikator dziedziczenia nie robi nic, poza poleceniem kompilatorowi zamknięcia i nie wyrzucania ostrzeżenia "ukrywasz tego członka". Pominięcie go nie będzie miało wpływu na skompilowany kod.

+1

Dobry punkt o wyciszaniu ostrzeżenia kompilatora! Jednak narzeka, ponieważ robienie czegoś takiego dla definicji klas może sprawić, że będą się zachowywać w sposób, jakiego się nie spodziewasz. W tym konkretnym przypadku nie mogę wymyślić żadnego niezamierzonego zachowania ... stąd pytanie. – user10789

+1

Tak. Oczywiście ostrzeżenie jest z jakiegoś powodu. Jedynym możliwym problemem jest, jak powiedziałem w odpowiedzi, fakt, że mamy do czynienia z dwoma różnymi slotami metod, które mogą rozwiązać różne implementacje. Jest to pułapka używania 'nowego'. Dla 'interfejsów 'nie jest to szkodliwe przez większość czasu, ponieważ nie zapewniają one żadnej implementacji, a wywołujący spodziewa się dowolnej implementacji metody. –

+1

Nowe słowo kluczowe nie służy do wyciszania kompilatora, ale jednoznacznie stwierdza, że ​​"nie chcę przesłonić żadnego członka, mam zamiar go śledzić i jestem tego świadomy". Ostrzeganiem jest powstrzymanie cię od "nieumyślnego ukrycia członka". –

0

Nie jestem pewien, czy to kompiluje, czy nie, ale nie jest to wskazane, wzór do naśladowania. Mając możliwość wykonania jawnej implementacji interfejsu, teoretycznie można dostarczyć dwie całkowicie różne implementacje dla wersji IReadOnly i ICanReadAndWrite dla właściwości Id. Rozważ zmianę interfejsu ICanReadAndWrite poprzez dodanie metody ustawiającej dla właściwości zamiast zastępowania właściwości.

+0

Zdecydowanie się kompiluje. Jeśli NIE dodasz nowego słowa kluczowego, otrzymasz następujące ostrzeżenie o kompilacji: "ICanReadAndWrite.Id" ukrywa dziedziczony element 'IReadOnly.Id'. Użyj nowego słowa kluczowego, jeśli chowanie było zamierzone. – user10789

+0

Jeśli chodzi o jawną implementację interfejsu, zakładam, że jeśli to zrobisz, dwie całkowicie różne implementacje właściwości Id są dokładnie tym, czego potrzebujesz. Jestem bardziej zainteresowany ukrytymi problemami lub mniej niż oczywistymi problemami, które mogą być spowodowane tym podejściem. – user10789

+1

Niestety, dodanie setera do właściwości nie będzie działać. Jeśli setter rzuca cień na gettera, odczytanie właściwości wymaga typograficznego typu z nie-cieniowanym geterem. Jeśli seter nie cienia pobierającego, tzn. Niezależny program pobierający i ustawiający są oba w zasięgu, żaden z nich nie będzie dostępny w C# lub vb.net. – supercat

1

Jest to całkowicie legalne i implikacje dla twojej klasy, które implementują interfejs ICanReadAndWrite, byłyby po prostu tym, że gdy jest traktowane jako IReadOnly, to może tylko czytać, ale gdy zostanie potraktowane jako ICanReadAndWrite, będzie w stanie zrobić oba.

0

Możesz to zrobić, ale nie jestem pewien, co masz nadzieję osiągnąć, robiąc to.

public IReadOnly SomeMethod() { 
    // return an instance of ICanReadAndWrite 
} 

Ta metoda zwróci odwołanie do IReadOnly co oznacza, że ​​nie ma znaczenia, że ​​zwrócił ICanReadAndWrite. Czy to podejście nie byłoby lepsze?

public interface IReadOnly 
{ 
    String GetId(); 
} 

public interface ICanReadAndWrite : IReadOnly 
{ 
    String SetId(); 
} 
+0

Chciałbym użyć prostej własności. – user10789

7

Nie powinieneś implementować ICanReadWrite na podstawie IReadOnly, ale zamiast tego uczynić je oddzielnymi.

tj.tak:

public interface IReadOnly 
{ 
    string Id 
    { 
     get; 
    } 
} 

public interface ICanReadAndWrite 
{ 
    string Id 
    { 
     get; 
     set; 
    } 
} 

Oto klasa korzystania z nich:

public class SomeObject : IReadOnly, ICanReadWrite 
{ 
    public string Id 
    { 
     get; 
     set; 
    } 
} 

Zauważ, że ta sama nieruchomość w klasie może obsługiwać oba interfejsy.

Należy zauważyć, że zgodnie z komentarzem, jedynym sposobem na uzyskanie solidnego rozwiązania będzie również posiadanie obiektu opakowania.

Innymi słowy, to nie jest dobre: ​​

public class SomeObject : IReadOnly, ICanReadWrite 
{ 
    public string Id 
    { 
     get; 
     set; 
    } 

    public IReadOnly AsReadOnly() 
    { 
     return this; 
    } 
} 

jako dzwoniący może po prostu to zrobić:

ICanReadWrite rw = obj.AsReadOnly() as ICanReadWrite; 
rw.Id = "123"; 

Aby uzyskać solidne rozwiązanie, trzeba obiekt otoki, jak poniżej:

public class SomeObject : IReadOnly, ICanReadWrite 
{ 
    public string Id 
    { 
     get; 
     set; 
    } 

    public IReadOnly AsReadOnly() 
    { 
     return new ReadOnly(this); 
    } 
} 

public class ReadOnly : IReadOnly 
{ 
    private IReadOnly _WrappedObject; 

    public ReadOnly(IReadOnly wrappedObject) 
    { 
     _WrappedObject = wrappedObject; 
    } 

    public string Id 
    { 
     get { return _WrappedObject.Id; } 
    } 
} 

To będzie działać i będzie solidne, aż do momentu, w którym osoba dzwoniąca używa refleksji.

+1

Ale wtedy nie można rzutować z ICanReadAndWrite na IReadOnly.Jest to bardzo przydatne, gdy masz klasę, która pracuje z obiektami do odczytu/zapisu, a następnie chcesz tylko zwrócić obiekt tylko do odczytu do konsumenta. – user10789

+0

Konsument może jednak po prostu odrzucić. To nie jest solidne. – recursive

+1

Oczywiście nie jest solidna, ale nie było oryginalne rozwiązanie, jeśli zadziałało. Jedynym sposobem na pozbycie się możliwości zapisu jest zwrócenie obiektu zawijania, który jawnie temu zapobiega, np. implementuje tylko IReadOnly. –

Powiązane problemy