Dai tuoi commenti sembra che tu abbia qualcosa di simile a questo:
Send(Data data, Address address, A a, B b, C c) { ... }
Send(Data data, Address address, A a, D d, E e, F f) { ... }
Send(Data data, Address address, B b, H h, J j) { ... }
Dove A
, B
, C
, ... sono tipi di valori utilizzati per la configurazione. Stai implicitamente lavorando con un tipo Configuration
che può venire in un insieme finito di "forme" o combinazioni. Desideri un unione taggata , ma C # non li supporta direttamente. Tuttavia, puoi implementarne uno con ereditarietà . La chiave è riconoscere che è possibile creare una classe astratta che abbia un insieme finito di sottoclassi di:
- Assegnare alla classe astratta un costruttore privato.
- Rendere le sottoclassi classi sigillate interne della classe astratta.
Le sottoclassi interne hanno accesso al costruttore privato della classe astratta, ma le classi di livello superiore no, quindi nessuno può aggiungere una nuova sottoclasse "dall'esterno".
Quindi avresti qualcosa di simile a questo:
abstract class Configuration {
private Configuration() {
// Prevent outside subclassing
}
public sealed class Conf1 : Configuration {
public A a { get; set; }
public B b { get; set; }
public C c { get; set; }
// Constructor boilerplate
}
public sealed class Conf2 : Configuration {
// Different fields
// Constructor boilerplate
}
// ... more subclasses
}
(Non lavoro regolarmente con C #, spero di non aver commesso gravi errori di sintassi.)
Quindi tutto ciò di cui hai bisogno è un modo sicuro per esaminare quale tipo di Configuration
sottoclasse hai ottenuto. Questo è essenzialmente il pattern del visitatore, ma puoi renderlo più conciso con i delegati:
abstract class Configuration {
...
public abstract R Match<R>(Func<Conf1, R> ifConf1, Func<Conf2, R> ifConf2, ...);
...
public sealed class Conf1 : Configuration {
...
public override R Match<R>(...) {
// Each subclass calls the delegate corresponding to it
return ifConf1(this);
}
...
}
// And so on with every other subclass
}
Ora puoi ridurre Send
in:
Send(Data data, Address address, Configuration config) {
return config.Match(
ifConf1: conf1 => { ... },
ifConf2: conf2 => { ... },
...
);
}
Tutto presuppone che il numero di combinazioni di valori di configurazione sia limitato e non cambi molto spesso. Ho assunto che, ad esempio, tutti i metodi Send
utilizzino lo stesso meccanismo sottostante e che possano essere semplicemente configurati in vari modi. Se volessi astrarre diversi tipi di servizi di invio (ad es. E-mail o qualche tipo di IPC vs SOAP), utilizzerei un interface
simile a MainMa's Sender
class poiché questa soluzione ti consente sempre di aggiungere facilmente nuovi tipi di" mittenti ".