2011-11-08 12 views
7

Czy jest możliwe zaimplementowanie klasy ograniczonej do dwóch unikalnych parametrów ogólnych?Ogólna klasa z dwoma nierównoległymi (unikalnymi) typami:

Jeśli tak nie jest, czy to dlatego, że jest niezaimplementowane, czy też nie byłoby możliwe ze względu na strukturę języka (dziedziczenie)?

chciałbym coś w postaci:

class BidirectionalMap<T1,T2> where T1 != T2 
{ 
    ... 
} 

ja wdrożenie Bidirectional dictionary. Jest to głównie kwestia ciekawości, a nie potrzeby.


sparafrazować z uwagami:

  1. Dan: "Jakie są negatywne konsekwencje, czy to ograniczenie nie jest spełniony"

  2. Ja: "Następnie użytkownik może indeksować za pomocą mapy [t1] i mapy [t2] .Jeśli byłyby tego samego typu, nie byłoby rozróżnienia i nie miałoby to żadnego sensu."

  3. Dan: Kompilator rzeczywiście pozwala [dwa ogólne parametry typu definiują przeciążenia różnych metod], więc jestem ciekawy; czy arbitralnie wybiera jedną z metod wywoływania?

+1

Zawsze można wyrzucić 'nowy ShouldBeCompileTimeError()' w konstruktorze . :-) – foson

+1

prawdopodobnie dlatego, że nie ma sensu, są to generics, coś, co nie powinno być "ograniczane" przez typy. – GriffinHeart

+0

Z ciekawości, jakie są negatywne konsekwencje, jeśli to ograniczenie nie zostanie spełnione? Mapowanie obiektów z tego samego zestawu jest bardzo powszechnym wymogiem. –

Odpowiedz

4

Rozszerzenie na przykład w celu zaznaczenia problemu!

public class BidirectionalMap<T1,T2> 
{ 
    public void Remove(T1 item) {} 
    public void Remove(T2 item) {} 

    public static void Test() 
    { 
     //This line compiles 
     var possiblyBad = new BidirectionalMap<string, string>(); 

     //This causes the compiler to fail with an ambiguous invocation 
     possiblyBad.Remove("Something"); 
    } 
} 

Więc odpowiedź brzmi, że choć nie można określić T1 = T2 wiązania, to nie ma znaczenia, ponieważ kompilator nie powiedzie się, gdy spróbujesz zrobić coś, co naruszy niejawne ograniczenie. Nadal łapie awarię podczas kompilacji, więc możesz bezkarnie korzystać z tych przeciążeń. To trochę dziwne, ponieważ możesz utworzyć instancję mapy (i może nawet napisać kod IL, który odpowiednio manipuluje mapą), ale kompilator C# nie pozwoli ci siać spustoszenie przez arbitralne rozwiązywanie niejednoznacznych przeciążeń.


Jedna uwaga jest taka, że ​​ten rodzaj przeciążenia może powodować dziwne zachowania, jeśli nie jesteś ostrożny. Jeśli masz BidirectionalMap<Animal, Cat> i kotów: zwierzę, należy rozważyć, co się stanie z tym kodem:

Animal animal = new Cat(); 
map.Remove(animal); 

To wywoła przeciążenie, że trwa zwierząt, więc będzie starał się usunąć klucz, choć mogłeś przeznaczone do usuń wartość Cat. Jest to dość sztuczny przypadek, ale wystarcza to, aby zachować ostrożność w przypadku bardzo różnych zachowań w wyniku przeciążenia metody. W takich przypadkach łatwiej jest je odczytać i zachować, podając tylko różne nazwy metod, odzwierciedlające ich różne zachowania (RemoveKey i RemoveValue, powiedzmy).

+0

Doskonała odpowiedź root! Tekst błędu:' Wywołanie jest niejednoznaczne między następującymi metodami lub właściwościami: BidirectionalMap .this [T] 'i' BidirectionalMap .this [K] '" – user664939

0

Więzy typu wydają się błędne. Podczas gdy powodują one, co jest parametrem typu, celem jest, aby kompilator wiedział, jakie operacje są dostępne dla danego typu.
Jeśli chcesz, możesz mieć ograniczenia, w których oba T1 i T2 pochodzą z oddzielnych konkretnych klas bazowych, ale nie sądzę, że to jest to, czego chcesz.

-1

Nie ma skutecznego sposobu na zrobienie tego bez nakładania jakichkolwiek ograniczeń na same typy. Jak ktoś inny zauważył, można wprowadzić ograniczenia, które dwa typy pochodzą z dwóch różnych klas bazowych, ale prawdopodobnie nie jest to zbyt dobre z punktu widzenia projektu.

Edytowane w celu dodania: powodem, dla którego nie jest to realizowane, jest najprawdopodobniej dlatego, że nikt w firmie Microsoft nigdy nie uważał czegoś takiego za konieczne do egzekwowania w czasie kompilacji, w przeciwieństwie do innych ograniczeń, które mają związek z tym, jak faktycznie jesteś w stanie używać zmiennych określonych typów. I jak zauważyli niektórzy komentatorzy, z pewnością można to wymusić w środowisku wykonawczym.

0

Nierówności nie pomogłyby kompilatorowi złapać błędów. Kiedy określasz ograniczenia dla parametrów typu, mówisz kompilatorowi, że zmienne tego typu zawsze będą obsługiwały określony interfejs lub będą zachowywać się w określony sposób. Każda z nich pozwala kompilatorowi potwierdzić coś bardziej podobnego "ta metoda będzie obecna, aby można ją było wywołać w T".

Nierówność parametrów typu bardziej przypomina sprawdzanie, czy argumenty metody nie są zerowe. Jest to część logiki programu, a nie jego typu.

0

Nie jestem do końca pewien, dlaczego byłby to pożądany czas kompilacji. Byłoby możliwe zasadniczo ominięcie warunku przez wybranie klucza lub wartości, co sprawiłoby, że sprawdzenie kompilacji stałoby się bezużyteczne.

Należy zastanowić się nad tym, jakie błędy próbuję zapobiec?

Jeśli po prostu zatrzymujesz leniwego współpracownika, nie czytając dokumentacji, a następnie dodaj opcję Tylko debugowanie i wyrzuć wyjątek. W ten sposób można usunąć kontrolę kodu zwolnienia, np.

#if Debug 

if (T1 is T2 || T2 is T1) 
{ 
    throw new ArguementException(...); 
} 

#endif 

Jeśli próbujesz zapobiec wrogą osobę z użyciem biblioteki w niezamierzony sposób, to być może jest potrzebna kontrola wykonawcze, w przeciwnym razie byłoby to łatwe do pola klucza lub wartości.

+0

Możesz również umieścić to sprawdzenie w konstruktorze statycznym typu ogólnego , więc uruchomi się tylko raz. (Twój test nie będzie się kompilował, nawiasem mówiąc, ponieważ lewa strona operatora 'jest' pobiera odwołanie do obiektu, a nie nazwę typu.)' if (typeof (T1) = = typeof (t2) ') – phoog

0

Nie, nie można użyć równości (lub nierówności) jako ograniczenia. Mówiąc najprościej, równość nie jest ograniczeniem, ale warunkiem. Należy przetestować pod kątem warunku, takie jak równość lub nierówność typów w konstruktorze i wyrzucić odpowiedni wyjątek.

Powiązane problemy