@Andrew Timson (+1) fa un punto eccellente sull'uso della riflessione (se possibile) e usando un'interfaccia.
Da un punto di vista del design orientato agli oggetti, le interfacce saranno un modo molto più piacevole per andare - a meno che, naturalmente, non ci sia abbastanza logica in comune / ereditata da garantire la sottoclasse. Questo potrebbe essere discusso come parte del " Principio di inversione delle dipendenze " al lavoro; "Dipende dalle astrazioni, non dipendere dalle concrezioni". Ti stai già inclinando verso una soluzione abbastanza pulita che aderisce a questo evitando la codifica dura, però, così bella!
In aggiunta a ciò, la riflessione potrebbe essere la strada da percorrere. Ci sono altre informazioni su quando il riflesso dovrebbe essere usato qui, su StackOverflow
Oltre a questo, potresti usare qualcosa come un Pattern osservatore adattato e avere ogni nuova istanza di una sottoclasse "register" stessa al momento dell'inizializzazione. Quindi c'è un oggetto che puoi interrogare per ottenere tutti gli oggetti in questione; potresti renderlo un Singleton per assicurarti che sia sempre accessibile per tutto il ciclo di vita dell'applicazione e per assicurarti che ci sia sempre un solo oggetto da interrogare.
Questa non è la soluzione migliore, ma è un'altra che potrebbe funzionare.