2014-09-12 10 views
7

Czy istnieje sposób na "hakowanie" lub "przymus" korektorów kowariancyjnych w C#?Obejście problemu z powodu braku kowariancji typu powrotu przy zastępowaniu metod wirtualnych

Na przykład:

public class Alpha { 
    public virtual Alpha DoSomething() { 
     return AlphaFactory.GetAlphaFromSomewhere(); 
    } 
} 
public class Beta : Alpha { 
    public override Beta DoSomething() { 
     return BetaFactory.GetBetaFromSomewhere(); 
    } 
} 

Niestety, C# nie obsługuje tej funkcji (co wydaje się nieco śmieszne, ale to ani tu ani tam).

Myślałem, że mam odpowiedź metodą ukrycia:

new public Beta DoSomething() { 
    return BetaFactory.GetBetaFromSomewhere(); 
} 

Ale to nie dodać wpis w do „vtable”, to po prostu w zasadzie deklaruje nową metodę o tej samej nazwie, i jako taki oznacza, że ​​dostęp do Beta s przez wskaźnik do Alpha wywoła Alpha.DoSomething().

A więc jakieś dobre sztuczki?

+2

Dlaczego trzeba zmienić wartość zwracaną? Co nie jest w stanie zaspokoić twoich potrzeb, ponieważ 'Beta.DoSomething()' zwraca obiekt typu "Alpha"? Dopóki właściwe metody są zdefiniowane jako wirtualne w 'Alfa', a następnie nadpisane w' Beta', tak naprawdę nie ma to znaczenia; każde wywołanie metody wirtualnej spowoduje rozpoznanie typu środowiska wykonawczego obiektu, a nie typu czasu kompilacji. – InBetween

+0

Nie rzuca wystarczająco dużo "hack"? – Mephy

Odpowiedz

3

Możesz zrobić całkiem niezłe rzeczy z rodzajami.

public class Alpha<T> where T: Alpha<T> { 
    public virtual T DoSomething() { 
     throw new NotImplementedException(); 
    } 
} 
public class Beta : Alpha<Beta> { 
    public override Beta DoSomething() { 
     throw new NotImplementedException(); 
    } 
} 
+0

Ogólne nadużycie, aby uzyskać dokładnie to, co? – InBetween

+0

Jeśli masz statyczne odwołanie do 'Beta', nie musisz wykonywać rzutowania w środowisku wykonawczym, aby uzyskać odniesienie' Beta' z 'DoSomething'. – recursive

+0

Och, więc komplikujesz cały typ systemu z tym ogólnym bólem głowy, aby po prostu uniknąć najtańszego możliwego rzutu (również bezpieczne)? – InBetween

0

W większości przypadków nie ma dużych korzyści w przypadku kowariancji zwrotu.

Jeśli dzwonisz wirtualny Alpha.DoSomething to naprawdę nie wiem, czy wracają się Alpha lub grupę Beta tak jak ma typ kowariancji pomocy powrócić? Będziesz musiał zapisać wynik w typowym obiekcie Alpha, ponieważ jest to jedyny typ, który może przyznać Alpha s lub Beta s.

Z drugiej strony, jeśli statycznie wiedzieć że dzwonisz Beta.DoSomething następnie można albo: bezpośrednio użyć zwrócony Alpha wpisane obiektu, ponieważ będzie dostępu do wspólnych metod funkcjonalności lub wirtualne więc nie obchodzi mnie, czy to naprawdę Beta (połączenie wirtualne zostanie ustawione na prawidłowy typ) lub można bezpośrednio wybrać (bezpieczny w czasie wykonywania) rzutowany na Beta w celu uzyskania dostępu do konkretnej implementacji.

Edytuj: usunięto problem z bezpiecznym typem. Ledwo to przemyślałem i ewidentnie powrót kowariancji typu byłby bezpieczny. Reszta odpowiedzi stoi jednak. Nie mogę znaleźć nic szczególnie interesującego w tej funkcji, przynajmniej w taki sposób, w jaki OP chce z niej skorzystać: uniknąć bezpiecznego taniego rzucenia?

+0

Java może to zrobić po wyjęciu z pudełka (pozwala zmienić typ zwracanej metody nieostatecznej w podklasach, o ile typ zwracany jest podklasą typu zwracanego w klasie bazowej). Uważam, że nierozsądne odrzucenie troski PO jako "bez wielkich korzyści" jest nieco nieudolne. –

+1

@KirkWoll [Wszystkie funkcje w C# zaczynają się od -100 punktów] (http://blogs.msdn.com/b/ericgu/archive/2004/01/12/57985.aspx), po prostu nie uzyskało wystarczającej liczby punktów. –

+0

@KirkWoll tylko dlatego, że Java robi to po wyjęciu z pudełka, niekoniecznie musi to być przekonujący powód. Zepsuta wariancja tablic w języku C# wynika również z jej istnienia w Javie ... Czy była to dobra decyzja projektowa? Mogę się mylić, a wariancja typu powrotu to wspaniała cecha w języku, więc proszę powiedz mi dlaczego. Twój komentarz nie jest konstruktywny ani nie dodaje niczego przydatnego w tym temacie. – InBetween

1

Zawsze można zawinąć metodę wewnątrz innej metody

public class Alpha { 
    public virtual Alpha DoSomething() { 
     return AlphaFactory.GetAlphaFromSomewhere(); 
    } 
} 
public class Beta : Alpha { 
    private Beta Helper() { 
     return BetaFactory.GetBetaFromSomewhere(); 
    } 
    public Beta DoSomething() { 
     return Helper(); 
    } 
    public override Alpha DoSomething() { 
     return Helper(); 
    } 
} 

Jeśli istnieje wiele metod, choć może chcesz autogenerować te owijarki. Jeśli to zbyt wymagające, rozwiązanie @recursive jest wszystkim, co zostało.

Powiązane problemy