2013-10-08 18 views
16

Po raz pierwszy zobaczyłem, że kolega robi to, gdy zaimplementował pule obiektów. Przekazał klasę, która miała zostać połączona jako parametr do ogólnej klasy bazowej. Ta klasa bazowa ułożyła kod łączenia.Czy pozwala klasie przejść się jako parametr do ogólnego zła klasy podstawowej?

Dziwne, że klasa bazowa będzie znała swoje dzieci. Jest to uważane za złą praktykę w każdym zwykłym przypadku. Ale w tym przypadku rodzic jest po prostu rozwiązaniem technicznym, aby uniknąć pisania powtarzalnego kodu. Klasa bazowa nigdy nie jest przywoływana przez żaden inny kod.

Jedną wadą tej konstrukcji jest to, że "pali klasę podstawową". Nie można wprowadzić ogólnej klasy bazowej w środku hierarchii. Ten problem może być poza tematem.

Poniżej jest do pomyślenia przykład:

public abstract class Singleton<T> where T : class 
{ 
    public static T Instance { get; private set; } 

    public Singleton() 
    { 
     if (Instance != null) 
      throw new Exception("Singleton instance already created."); 
     Instance = (T) (object) this; 
    } 
} 

public class MyClass : Singleton<MyClass> 
{ 
} 

kod Poprawiona:

public abstract class Singleton<T> where T : Singleton<T> 
{ 
    public static T Instance { get; private set; } 

    public Singleton() 
    { 
     if (Instance != null) 
      throw new Exception("Singleton instance already created."); 
     Instance = (T) this; 
    } 
} 

public class MyClass : Singleton<MyClass> 
{ 
} 

Odpowiedz

14

Nie; jest to dobrze znany wzór o nazwie CRTP.
Jest to szczególnie przydatne w C++ jako alternatywa dla metod wirtualnych.

Można go zobaczyć w środowisku .Net w IComparable<T> i IEquatable<T>.

Dla zwiększenia niezawodności, należy dodać where T : Singleton<T>

+0

Wysadzasz mój umysł tym ostatnim zdaniem. klasa SingleTon gdzie T: Singleton ? Czy nie wymagałoby to ciągłej rekurencji (każdy T musiałby być Singletonem , gdzie T byłby znowu singletonem , itp.) – Kristof

+0

Dodano ulepszenie kodu SLKS. Pierwsza linia sprawia, że ​​twoja głowa się kręci. –

+0

@Kristof Zobacz przykład: 'public MyClass: Singleton ' powyżej.Typ, 'MyClass' IS-A' Singleton 'tak to działa. Mimo to wciąż jest trochę melodyjna. –

3

SLaks jest poprawne - to przydatny wzór, na ogół, gdy chcesz zapewnić kodu w klasie bazowej, która jest silnie wpisany do klasy pochodnej.

Powinieneś również dodać ograniczenie typu do parametru ogólnego, aby wskazać, że typ ogólny musi dziedziczyć po typie abstrakcyjnym. Składnia do dodania tego ograniczenia wygląda rekurencyjnie, ale nie panikuj o tym - nie jest rekurencyjnie analizowana i po prostu zapewnia, że ​​jedynymi prawidłowymi rodzajami generowanymi są klasy pochodne.

Na przykład załóżmy, że prowadzisz biznes mieszania herbaty i kawy. Sensowne jest mieszanie kawy z kawą i herbatą z herbatą, ale chcesz się upewnić, że nie można mieszać kawy z herbatą. Jednak ponieważ oba są napojami, chcesz je modelować w ten sam sposób.

public abstract class Beverage<T> where T : Beverage<T> 
{ 
    public abstract T Blend(T drink1, T drink2); 
} 

public class Tea : Beverage<Tea> 
{ 
    public override Tea Blend(Tea drink1, Tea drink2) 
    { 
     // Blend tea here. 
    } 
} 
public class Coffee : Beverage<Coffee> 
{ 
    public override Coffee Blend(Coffee drink1, Coffee drink2) 
    { 
     // Blend coffee here. Although coffee is nasty, so 
     // why you'd want to is beyond me. 
    } 
} 

Po przeczytaniu o CRTP, warto pamiętać, że C++ szablony są tylko powierzchownie podobny do C# rodzajowych. Podstawową różnicą jest to, że szablony są efektywnie narzędziem generowania kodu, które działa podczas kompilacji, podczas gdy generics C# są obsługiwane w czasie wykonywania.

Ponadto pisanie takiego kodu może zmniejszyć czytelność. Więc chociaż istnieją zdecydowanie przypadki, w których byłoby to właściwe podejście, należy pomyśleć o problemie, który próbujesz rozwiązać, i sprawdzić, czy istnieje bardziej proste podejście.

+0

MyClass.Instance kompiluje. –

+0

Zmieniono odpowiednio. –

+2

Oczywiście "publiczna klasa kawy: napój " jest deklaracją prawną. Łatwo to zepsuć. – Brian