2012-01-12 13 views
8

Możliwe, że nie rozumiem umów na kod, ale oto moja sytuacja.Kontrakty dotyczące kodu i problemy związane z dziedziczeniem, co dalej?

Mam następujący kod:

interface IFetch<T> // defined in another DLL 
{ 
    T Fetch(int id); 
} 

interface IUserFetch : IFetch<User> 
{ 
    IEnumerable<User> GetUsersLoggedIn(); 
} 

class UserFetch : IUserFetch 
{ 
    public User Fetch(int id) 
    { 
     return (User) Database.DoStuff (id); 
    } 

    public IEnumerable<User> GetUsersLoggedIn() 
    { 
     return (IEnumerable<User>) Database.DoMoreStuff(); 
    } 
} 

Próbuję dodać stosunkowo prostą umowę: Contract.Requires (id != 0);, i chcę to potwierdzone na Fetch. Kiedy dodaję go bezpośrednio do Fetch, otrzymuję ostrzeżenie o Method Fetch(int id) implements interface 3rdParty.IFetch<User> and thus cannot add Requires.

Utworzono abstrakcyjną klasę kontraktów kodu implementującą IFetch i wskazałem ją do/z UserFetch, używając odpowiednio atrybutów ContractClass i ContractClassFor. Nadal dostaję błąd taki jak CodeContracts: The class 'FetchUserContracts' is supposed to be a contract class for '3rdParty.IFetch<User>', but that type does not point back to this class. Jednak ponieważ 3rdParty.IFetch jest typem ogólnym, nie sądzę, abym kiedykolwiek mógł w tym celu zawrzeć umowę kodu.

Czy problem został jasno, a jeśli tak, to jak mogę go rozwiązać?

+0

Prawdopodobny duplikat: http://stackoverflow.com/questions/3414586 –

+0

@RobertHarvey: W większości przypadków biorę to pod uwagę, ale mój problem wydaje się być bardziej związany z rodzajami leków –

Odpowiedz

5

Wierzę odpowiedź Ɖiamond ǤeezeƦ jest prawidłowe. Dodam tylko trochę wyjaśnień.

Nie można dodać dekoracji w stylu w zamkniętym typie konstrukcji (takim jak IFetch<User>). Musisz dodać go do otwartego typu konstrukcji (IFetch<>). Powodem tego jest to dokładnie z tego samego powodu, że nie można dodać Contract.Requires dekorację do konkretnej metody, która jest używana do implementuje interfejs: umowy code są przeznaczone do zweryfikowania w czasie kompilacji, gdy pełny typ instancji obiektu może być nieznane.

Załóżmy, że nie wolno Ci umieścić kontrakt od konkretnej implementacji metody.

public User Fetch(int id) 
{ 
    Contract.Requires (id != 0);, 
    return (User) Database.DoStuff (id); 
} 

Załóżmy teraz, że ktoś próbował użyć zmiennej typu interfejsu.

class DataDisplayer<T> 
{ 
    Label myLabel = new Label(); 
    public void Display(IFetch<T> fetch, int id) 
    { 
     myLabel.Text = fetch.Fetch(id).ToString(); 
    } 
} 

Czy pozwala to na naruszenie umowy, czy nie? Nie można tego powiedzieć, ponieważ nie wiemy, jaki konkretny typ fetch będzie w środowisku wykonawczym.

Umieszczenie umowę na konkretnym zamkniętym wybudowanym typu jak IFetch<User> nie rozwiąże tego problemu. Nadal nie wiemy, jaki typ T znajduje się w kodzie wywołującym.

Zasada podstawiania Liskov, która jest kamieniem węgielnym programowania obiektowego, oznacza, że ​​nigdy nie możemy zakładać niczego na temat typu obiektu wykonawczego poza tym, co wskazuje typ zmiennej. A ponieważ typ zmiennej może być interfejsem generycznym, umowy kodu nie mogą nakładać dodatkowego obciążenia na element wywołujący, niż ma to miejsce w definicji interfejsu ogólnego.

Dlatego na otwartym typie należy umieścić Contract.Requires, aby każdy obiekt implementujący interfejs miał wymagane wymagania, a wszelkie potencjalne wywołania metody za pomocą zmiennej typu interfejsu można zweryfikować pod kątem poprawności do tego wymogu.

10

Musisz utworzyć klasę abstrakcyjną do realizacji zamówienia, na przykład:

[ContractClassFor(typeof(IFetch<>))] 
public abstract class ContractClassForIFetch<T> : IFetch<T> 
{ 
    public T Fetch(int id) 
    { 
     Contract.Requires(id != 0); 
     return default(T); 
    } 
} 

i dodaj następujący ContractClass atrybut IFetch:

[ContractClass(typeof(ContractClassForIFetch<>))] 
public interface IFetch 
{ 
    T Fetch(int id); 
} 
+0

Zobacz także [powiązane pytania/odpowiedzi] (https: //stackoverflow.com/questions/3414586/) – orad

Powiązane problemy