Uso pratico dell'ereditarietà generica e interazioni con i non generici?

1

Ho cercato di saperne di più sui farmaci generici, e alla fine ho sentito che mi sono imbattuto in una situazione che sarebbe stata più utile usarla invece che solo nelle classi regolari. Il seguente sarebbe un uso pratico dei farmaci generici?

Un'astronave ha punti di attacco che possono equipaggiare solo determinati tipi di equipaggiamento. La funzione equip è dotata di codice per determinare se il punto fisso può equipaggiare un oggetto in base a quale parametro T è assegnato.

class Equipment {
...
}

class Thruster : Equipment {
...
}

class Spaceship {
    void Equip<T>(Hardpoint<T> hardpoint, Equipment equipment) {
        hardpoint.Equip(equipment);
    }
}

class Hardpoint<T> where T : Equipment {
    void Equip(Equipment equipment) {
        ...(check if equipment inherits from or is of type T)
    }

    ... (some other code common to all hardpoints)
}

class ThrusterHardpoint : Hardpoint<Thruster> {
    .... (some code specific to thruster hardpoints)
}
    
posta Zabytus 06.05.2014 - 15:18
fonte

2 risposte

3

Questo bit non mi piace:

void Equip(Equipment equipment) {
    ...(check if equipment inherits from or is of type T)
}

E questa è la stessa cosa che @MikeSW apparentemente intendeva, ma non ha elaborato ed è stato ridimensionato.

Se si dispone di generici (reificati in questo modo, in C #), perché si lascia il controllo dei caratteri fino al runtime?

The equip function has code to determine if the hardpoint can equip an equipment item

... o stai facendo manualmente il lavoro che il sistema di tipi dovrebbe fare per te e stai rinunciando alla sicurezza del tipo in fase di compilazione. Con quale buon fine? Cosa giustifica questa scelta di design?

    
risposta data 06.05.2014 - 15:52
fonte
-2

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.

    
risposta data 07.05.2014 - 04:45
fonte

Leggi altre domande sui tag