2010-05-17 8 views
8

To dość zastanawiające, aby przekonać się, że ograniczenia rodzajowe nie mogą zostać przeniesione do jego typu pochodnego.Dlaczego nie można przenosić własności generycznych na ograniczenie generyczne?

Powiedzmy mam następujący kod:

public abstract class BaseClass 
{ 
    public int Version 
    { get { return 1; } } 

    public string FixString { get; set; } 

    public BaseClass() 
    { 
     FixString = "hello"; 
    } 

    public virtual int GetBaseVersion() 
    { 
     return Version; 
    } 
} 

public class DeriveClass: BaseClass 
{ 
    public new int Version 
    { get { return 2; } } 
} 

I wiecie co, ta metoda zwróci błąd kompilacji:

public void FreeConversion<T>(T baseClass) 
    { 
     if(baseClass.GetType()==typeof(DeriveClass) 
     var derivedMe = (DeriveClass)baseClass; 
    } 

musiałbym rzucić baseClass do object pierwszy przed I może obsłużyć go do DerivedClass, tj.

public void FreeConversion<T>(T baseClass) 
    { 
     if(baseClass.GetType()==typeof(DeriveClass) 
     var derivedMe = (DeriveClass)((object)baseClass); 
    } 

Wydaje mi się dość brzydka. Dlaczego tak się dzieje?

Odpowiedz

7

Po pierwsze, nie należy rzucać zmiennej typu bazowego do typu pochodnego. To nie powinno działać, tylko na odwrót.

Po drugie, dlaczego działa poprzez object, jest spowodowane usunięciem kontroli typu podczas kompilacji. Kompilator może sprawdzić, czy BaseType nie można przesłać do DerivedType. Ale gdy zmienna to object, kompilator pozostawia ją przy założeniu, że wiesz co robisz. Nawet jeśli się skompiluje, kod ulegnie awarii podczas wykonywania.

3

Odpowiedź jest prosta: kompilator nie może wiedzieć, że T w metodzie FreeConversion można przekonwertować na DeriveClass.

+0

Dlaczego kompilator nie może o tym wiedzieć? Myślałem, że powinien wiedzieć, ponieważ wyraźnie określić, że 'DeriveClass' jest dziedziczone klasy dla' BaseClass' – Graviton

+0

@ Nu: Nie, nie. Możesz * przetestować *, aby zobaczyć * jeśli * 'T' jest typu' DeriveClass', a następnie - * niezależnie od testu * - próbujesz jawnie rzucić 'T' na' DeriveClass'. Pamiętaj, że kompilator nie * uruchamia * twojego kodu - po prostu * kompiluje *. Co można zrobić, jest konwertowane za pomocą klauzuli "as" zamiast jawnej obsady. 'baseClass as DeriveClass' zwróci instancję jako' DeriveClass' jeśli to możliwe, i 'null' inaczej, ale jeśli sprawdzisz, czy jest to możliwe jako pierwsze (jak teraz), to wiesz (nawet jeśli twój kod nie zawiera), że będzie nigdy nie powinno mieć wartości "null". –

+0

@Ngu Kompilator nie może wiedzieć, ponieważ nikt nie może wiedzieć! Gdy szablon jest tworzony, nie ma nic do powiedzenia, że ​​kiedykolwiek otrzyma tylko parametr BaseClass i na pewno nic nie powie, że będzie to wartość DerivedClass. Nie ma na przykład nic, co mogłoby powstrzymać kogoś nazywającego "FreeConversion (42)". Jedynym wyjątkiem jest metoda członkowska szablonu ograniczonego do wyprowadzenia z własnego parametru (w niektórych przypadkach przydatna). –

1

Jak już wspomniano, najtańszą opcją jest pierwszy rzut do obiektu, a następnie typ, który chcesz wykonać. Brzydki, ale działa.

Poza tym może się zdarzyć, że naruszysz zasadę Liskov Substitution, nic, co zaszkodzi jakiemukolwiek zwierzęciu, ale może doprowadzić twój projekt do niemożliwego do utrzymania kodu.

trzecie, miły podstęp, żeby pozwolić klasa bazowa narazić typu pochodnego jest coś takiego:

public class Base<T> where T : Base<T> { 
    T IAmDerived; 
} 

public class Derived : Base<Derived> { } 
0

Przede wszystkim w swojej generycznej metody typu T mogłaby być typu vale lub oznaczenie typu. Powodem, dla którego pozwala to zrobić za pomocą "Object", jest to, że po prostu robisz box-unboxing, który działa dla dowolnego typu w systemie.

Po drugie, to będzie straszny pomysł, aby przekonwertować/rzucić obiekt baseclass na klasę pochodną. Naruszasz mechanikę OOP.

Jeśli naprawdę chcesz zwrócić obiekt typu pochodnego od klasy bazowej, oto jeden z możliwych sposobów - rozwiązanie jest prawie podobne do tego, co zaproponował Frank.

//This is how you create a function in BaseClass that returns the collection 
//of DerivedTypes when called from an object of type derivedclass, no magic just Generics. 

//**The BaseClass** 
public class BaseClass<T> 
    where T : BaseClass<T> 
{ 
    public HashSet<T> GetHashSet() 
    { 
     HashSet<T> _hSet = new HashSet<T>(); 
     //do some work    
     //create a HashSet<T> and return;    
     return _hSet; 
    } 
} 
//**The Derived Class** 
public class DerivedClass : BaseClass<DerivedClass> 
{ 
    //you have the method inherited. 
} 
Powiązane problemy