Non vedo alcun punto sul tuo uso dei generici nel tuo esempio. Stai anche seguendo una trappola con il tuo utilizzo qui:
class Thruster : Hardpoint<Thruster>
Questo è simile alla creazione di una classe
class EmployeeList : List<Employee>
Non vi è alcun motivo per farlo perché Elenco consente di specificare il Dipendente una sola volta nell'utilizzo, non per ogni caso separato. Considera questo per esprimere il mio punto. Se avevi un metodo che prendeva come parametro un tuo tipo EmployeeList, ma tutto ciò che avevi era un IEnumerable dovevi prima costruire questo nuovo tipo e popolarlo prima di poterlo passare ai tuoi metodi. Questo è un lavoro extra per programmatore, compilatore e CPU. Questo mi serve molto meglio.
public static void DoSomething(this IEnumerable<Employee> value) { }
Se l'ordine conta, sarebbe.
public static void DoSomething(this IOrderedEnumerable<Employee> value) { }
Se gli indici sono necessari.
public static void DoSomething(this IList<Employee> value) { }
Ora funziona per qualsiasi tipo di elenco, non solo per il tuo specifico tipo EmployeeList.
Quindi nel codice lo usi invece in questo modo:
var employees = new List<Employee>();
employees.Add(new Employee(1));
// Notice this code doesn't need this extra redundant type EmployeeList at all.
Nell'esempio precedente il generico era utile in modo che il metodo Add avesse un tipo che si aspetta. Hai specificato il tipo quando hai dichiarato l'istanza utilizzando il suo costruttore predefinito. Quindi in pratica questo ti evita di dover gettare i tuoi oggetti in qualcosa, e anche in questo modo l'intellisense e il compilatore possono assicurarsi di passare effettivamente le istanze di Employee nel metodo Add di un'istanza di List.
Prima che esistessero i generici, dovevi farlo invece per l'esempio precedente:
var employees = new List();
employees.Add(new Employee(1));
employees.Add(new Car(2)); // Compiler won't see this as an error, because a Car is an object.
Console.WriteLine(((Employee)employees[0]).EmployeeID); // Have to case anything coming out of the List because compiler thinks they are just objects, not Employee instances.
Come puoi vedere, la vecchia modalità ti consente di fare una brutta cosa, aggiungi istanze Car ad una lista di Employee. Inoltre, richiedeva al programmatore di digitare di più e trasmettere tutto manualmente. In questo caso esploderebbe se lo facessi:
Console.WriteLine(((Employee)employees[1]).EmployeeID); // Subset item 1 is not an instance of Employee, cast will fail.
EDIT: aggiunta spiegazione del perché la classe EmployeeList è extra e ci sono soluzioni migliori per le operazioni basate su set specifici del dominio.