2010-03-04 11 views
37

Rozumiem, że interfejsy są umowami, a wszelkie zmiany (nawet dodatki) łamią każdy zależny kod. Mogłabym jednak przysiąc, że przeczytałem jakiś czas temu, że jedna z ostatnich wersji .NET (3, 3.5 ??) dodała nowy atrybut, który można zastosować do nowych członków interfejsu. Ten atrybut umożliwiał wersjonowanie i/lub dodawanie członków jako opcjonalnych. Byłoby to coś w rodzaju:C# Interfejsy z opcjonalnymi metodami

interface ITest 
{ 
    void MethodOne(); 

    [InterfaceVersion(2)] 
    void MethodTwo(); 
} 

Wyglądałem wysoko i nisko, ale po prostu nie mogę tego znaleźć. Zastanawiam się, czy po prostu źle zrozumiałem to, co myślę, że czytam i nie ma czegoś takiego. Czy ktoś ma wgląd?

+0

Ok, przytłaczające głosowanie brzmiało "nie, ale ...". Będę więc winić to za długi dzień, a ja będę zmęczony. :) –

+0

Myślałem trochę więcej o tym, jak/jeśli to może działać. O ile się nie mylę, tylko kompilator konsumencki musiałby "wspierać" to. Kompilator interfejsu po prostu doda atrybut do złożenia, podczas gdy kompilator konsumencki będzie tym, który wymusi implementacje. Prawie (choć nie technicznie) byłoby "kodowanie cukru", aby zapobiec ITest1, ITest2, itp. –

+0

Konsument musiałby określić, której wersji używa, ale wtedy masz pewne trudności. A co jeśli twój kod używa obu wersji? Więc masz Class1: [InterfaceVersion (1)] ITest i Class2: [InterfaceVersion (2)] ITest. Następnie robisz "jeśli (Class2 jest ITest)" ... ale który? To tak, jak raz użyjesz atrybutu w jednym miejscu, MUSISZ użyć go WSZĘDZIE. Kompilator musiałby śledzić to wszystko i zmuszać go do działania. Być może łatwiej będzie robić ITest1, ITest2 ... Tak więc ten nowy atrybut "wygody" może w rzeczywistości pogorszyć sytuację. :) –

Odpowiedz

41

Należy utworzyć dwa interfejsy:

interface ITest 
{ 
    void MethodOne(); 
} 

interface ITest2 : ITest 
{ 
    void MethodTwo(); 
} 

to również jasno, którego funkcjonalność wymaga którą wersję interfejsu, dzięki czemu nie trzeba by sprawdzić, czy klasa wykonawczych interfejs realizuje tylko jedną lub obie metody.

+0

Widziałem to już w dniach COM. Po kilku latach może stać się długą listą, ale do tego czasu możesz przeprojektować swoje API. –

+0

Ale to pokonuje cel mający wspólne prawo umowne. Chodzi mi o to, że powinienem móc przekazać obiekt implementujący jeden z tych dwóch interfejsów (ITest, ITest2) do funkcji bez brzydkiej obsady. Myślę, że posiadanie pojedynczego interfejsu (który omawia wszystkie metody) w tym przypadku ITest2 i tych, którzy chcieliby zaimplementować tylko kilka funkcji, po prostu musiałoby rzucić wyjątek NotSupportedException dla reszty, jak opisano tutaj https://stackoverflow.com/a/ 4566711/3921536 – router

+0

Wyjątek NotSupportedException/NotImplementedException pod względem interfejsu musi być ostatnim rozwiązaniem, do którego należy się zwrócić, gdy nie są dostępne żadne inne sposoby struktury kodu. Jest to równoznaczne z powiedzeniem "Pewnie, że mogę poprowadzić cię do pracy jutro", a potem pojawił się rower z napisem "Fooled you!". Interfejs powinien być traktowany jako umowa, jeśli mówisz, że go wspierasz, naprawdę ** powinnaś go wspierać. W niektórych przypadkach programiści uzasadnili używanie NotSupportedException, ale to powinno być stosowane tylko w ostateczności. –

4

Nie znam takiego atrybutu, który pozwala częściowo implementować implementację interfejsu. Można to obejść za pomocą klasy abstrakcyjnej, ale:

public abstract class Test 
{ 
    public abstract void MethodOne(); 
    public virtual void MethodTwo() { } 
} 

Pozwoliłoby to użytkownikowi zdecydować, czy chcą, aby zastąpić MethodTwo gdy dziedziczenie z testem, a zmuszając Nadrzędnym MethodOne.

+0

Czasami to rozwiązanie, choć na pewno nie jest źle, może złamać istniejący kod z powodu braku dziedziczenia wielokrotnego. –

+0

Tak, to jest podobne do tego, co powiedział Kilhoffer. Jestem tego świadomy, ale zazwyczaj unikam klas bazowych, chyba że moja nowa klasa "jest" BaseClass (versus "ma" ISomething "). –

5

Nie ma takiego atrybutu w środowisku .NET Framework.

+1

Przeszedłem przez każdy atrybut na http://msdn.microsoft.com/en-us/library/aa311259%28VS.71%29.aspx i nie znalazłem takiego, który by się wyróżniał. Na razie będę musiał się z tobą zgodzić. –

10

Nie widziałem takiego atrybutu, ale myślę, że to możliwe. This article na MSDN opisuje wersjonowanie za pomocą słów kluczowych overrides i new.

W skrócie, język C# jest wyposażony w funkcje językowe, które umożliwiają ewolucję klas pochodnych i nadal zachowują zgodność. Ten przykład pokazuje czysto bazową relację, ale baza faktycznie implementowałaby interfejs potrzebny do wersji. Posiadanie jednego interfejsu wymaga innej (poprzedniej wersji) interfejsu sprzężonego z tą metodą również jest bardzo użyteczny.

Przykład tworzenia interfejsu, który wymaga innego:

public interface IMyInterface 
{ 
    void FirstMethod(); 
} 

public interface IMySecondInterface : IMyInterface 
{ 
    void SecondMethod(); 
} 

Przykład użycia dziedziczenia aby zachować kompatybilność:

public class MyBase 
{ 
    public virtual string Meth1() 
    { 
     return "MyBase-Meth1"; 
    } 
    public virtual string Meth2() 
    { 
     return "MyBase-Meth2"; 
    } 
    public virtual string Meth3() 
    { 
     return "MyBase-Meth3"; 
    } 
} 

class MyDerived : MyBase 
{ 
    // Overrides the virtual method Meth1 using the override keyword: 
    public override string Meth1() 
    { 
     return "MyDerived-Meth1"; 
    } 
    // Explicitly hide the virtual method Meth2 using the new 
    // keyword: 
    public new string Meth2() 
    { 
     return "MyDerived-Meth2"; 
    } 
    // Because no keyword is specified in the following declaration 
    // a warning will be issued to alert the programmer that 
    // the method hides the inherited member MyBase.Meth3(): 
    public string Meth3() 
    { 
     return "MyDerived-Meth3"; 
    } 

    public static void Main() 
    { 
     MyDerived mD = new MyDerived(); 
     MyBase mB = (MyBase) mD; 

     System.Console.WriteLine(mB.Meth1()); 
     System.Console.WriteLine(mB.Meth2()); 
     System.Console.WriteLine(mB.Meth3()); 
    } 
} 
+0

Znałem tę metodę, ale nie używam interfejsów. Otrzymałem tak wiele odpowiedzi mówiąc "nie", że musiałem źle to odczytać. –

+0

Klasa podstawowa może implementować oryginalny interfejs – Kilhoffer

+0

To prawda. Myślę jednak, że może to być trochę mylące, jakie są twoje intencje, nawet jeśli jest to całkowicie poprawne. W niektórych okolicznościach może to być najlepszy sposób (nie ma "srebrnego bulletu", który zawsze jest najlepszym rozwiązaniem). –

0

Można myśleć o partial methods (MSDN).

+0

Nie, ale nawet nie wiedziałem, że możesz to zrobić. Częściowe klasy są używane w całości (kod konstruktora kontra wygenerowany przez użytkownika).Dowiedziałem się o tak wielu "przypadkach skrajnych" w czytaniu C#/.NET za pośrednictwem tego forum! –

2

Niedawno był w sytuacji, gdy podyktowane brakiem wielokrotnego dziedziczenia zabronił mi przekształcić istniejący interfejs do klasy abstrakcyjnej, i znalazłem się z rozciągającym się rozwiązania:

interface IFoo { 
      int RowCount(); 
    } 

    static class _FooExtensions { 
      public static bool HasAnyRows (this IFoo foo) { 
        return foo.RowCount() > 0; 
      } 
    } 

w ten sposób można zapewnić domyślna wersja w przypadku, gdy Twoja metoda abstrakcyjna może być zdefiniowana pod względem innych funkcji.

+0

Tak, to byłoby jak klasa pomocników. Na początku myślałem, że odnosisz się do metod rozszerzania, które w niektórych sytuacjach działałyby dobrze (LINQ, itp.). Jedna rzecz, która się wyróżniała, co robi "to IFoo foo"? Użyłem tego, aby uzyskać dostęp do członków instancji, w tym przeskakiwanie z jednego konstruktora do drugiego, ale nigdy nie widziałem, żeby był używany w ten sposób. –

+0

@Nelson: jest to metoda rozszerzenia. Nie są używane tylko dla LINQ. –

+0

Ups, moje złe. Użyłem metody rozszerzenia raz lub dwa razy, ale nie pamiętałem pełnej składni. Rozumiem, że ma wiele zastosowań. Nadal technicznie jest to klasa "pomocnicza", ale utrzymująca koncepcje OOP bez faktycznego implementowania interfejsu lub wywodzenia się z klasy. Bardzo przydatny czasami. –

1

Mogłeś przeczytać coś podobnego

interface ITest 
{ 
    void MethodOne(); 

    [InterfaceVersion(2)] 
    void MethodTwo(); 
} 



[AttributeUsage(AttributeTargets.All)] 
public class InterfaceVersion : System.Attribute 
{ 
    public readonly int N; 

    public InterfaceVersion(int n) 
    { 
     this.N = n; 
    } 
} 

Ale nie sądzę, że mógłby realizację MethodTwo opcjonalnym.

EDIT:

Właśnie dowiedziałem się przez uruchomienie kodu, który tak naprawdę nie robi realizację MethodTwo opcjonalnym.

+0

W porządku, kompilator nie zezwala na to, aby był opcjonalny, chyba że Microsoft został specjalnie zintegrowany (np. [Przestarzałe()] itp.). Nawet wtedy nie jestem pewien, jak to będzie działać i może być niemożliwe. –

+0

Do edycji: tak, to po prostu niestandardowy atrybut, który ma w nazwie "Wersja". Równie dobrze można by go nazwać "InterfaceNumber" i mieć dokładnie taki sam efekt. –

6

Być może myślisz o nowej funkcji "no pia" w C# 4? Oznacza to, że zezwalamy na "dołączanie" tylko części interfejsu, z którego faktycznie korzystasz z PIA, a następnie możesz pominąć wysyłkę PIA do swoich klientów. Jeśli robisz to kilka razy w kilku różnych złożeniach, CLR wykonuje pracę, aby dowiedzieć się, że wszystkie te połączone częściowe interfejsy są logicznie tego samego typu i ujednolica je. W ten sposób możesz przekazywać obiekty, które implementują każdy smak interfejsu z jednego zestawu do drugiego i wszystko działa. Jednak interfejsy oryginalne, z których tworzone są interfejsy "no pia", muszą być takie same.

+0

Nie sądzę, że to było to, ale brzmi interesująco. Czy przypadkiem masz link do kilku dodatkowych informacji (przykładów itp.)? –

+1

@Nelson: http://blogs.msdn.com/samng/archive/2010/01/24/the-pain-of-deploying-primary-interop-assemblies.aspx –

Powiązane problemy