2013-07-24 13 views
5

Ho un enum che sto cercando di associare ad DTO:Utilizzare un enum per scegliere quale classe istanziare

public enum DtoSelection 
{ 
    dto1, 
    dto2, 
    dto3, 
} 

ci sono 108 ed i valori in questo enum.

Ho un oggetto dto per ciascuno di questi DTO:

public class dto1 : AbstractDto 
{ 
     public int Id { get; set; } 
     //some stuff specific to this dto 
} 

Sto cercando di fare un metodo (eventualmente un servizio) che mi restituirà un nuovo oggetto dto del tipo associato al l'dto domanda:

private AbstractDto(int id) 
{ 
     if (id == DtoSelection.Dto1.ToInt()) //extension method I wrote for enums 
      return new Dto1(); 
     if (id == DtoSelection.Dto2.ToInt()) 
      return new Dto2(); 
} 

Ovviamente non voglio farlo 108 volte. Per qualsiasi ragione il mio cervello manca semplicemente qualcosa di ovvio. Qual è il modo migliore per gestire questo.

+0

Sono curioso di sapere se questo è possibile – Jonesopolis

+0

Per prima cosa puoi migliorare la tua lista 'if' usando' switch'. E vuoi usare la riflessione? –

+1

Qual è il ragionamento alla base di 108 diversi oggetti dto in un enum? perché non usare un array? – chancea

risposta

3

Questa classe sarà fare quello che vuoi finché le classi Dto sono definite nello stesso spazio dei nomi di AbstractDto (sarà necessario modificarlo se non lo sono):

Dati i seguenti enumerazioni e classi:

public enum DtoSelection 
{ 
    Dto1, 
    Dto2, 
    Dto3, 
} 

public abstract class AbstractDto 
{ 
} 

public class Dto1 : AbstractDto 
{ 
} 

public class Dto2 : AbstractDto 
{ 
} 

public class Dto3 : AbstractDto 
{ 
} 

Questo metodo risolverli:

public static class DtoFactory 
{ 
    public static AbstractDto Create(DtoSelection dtoSelection) 
    { 
     var type = Type.GetType(typeof(AbstractDto).Namespace + "." + dtoSelection.ToString(), throwOnError: false); 

     if (type == null) 
     { 
      throw new InvalidOperationException(dtoSelection.ToString() + " is not a known dto type"); 
     } 

     if (!typeof(AbstractDto).IsAssignableFrom(type)) 
     { 
      throw new InvalidOperationException(type.Name + " does not inherit from AbstractDto"); 
     } 

     return (AbstractDto)Activator.CreateInstance(type); 
    } 
} 
4

Utilizzare il metodo Activator.CreateInstance e passarlo al valore ToString di enum.

Type type = Type.GetType(DtoSelection.dto1.ToString()); 
var temp = Activator.CreateInstance(type); 
+0

Mi piace. Dovrei farlo 108 volte per ogni dto. – Robert

+0

@Robert, puoi avere un ciclo sui membri di enum e quindi creare l'istanza e aggiungerla in un elenco, per il ciclo vedi: http://stackoverflow.com/questions/972307/can-you-loop-through-all- enum-values ​​ – Habib

1

Vorrei utilizzare un dizionario di funzioni.

Dictionary<DtoSelection, Func<AbstractDto>> dictionary = 
     new Dictionary<DtoSelection, Func<AbstractDto>> 
{ 
    {DtoSelection.dto1,() => new dto1()} 
}; 

var dto = dictionary[DtoSelection.dto1](); 
+0

Sto cercando di evitare di scrivere 108 volte. Avrei bisogno di costruire un dizionario di tutti i tipi. – Robert

1

Provare a utilizzare Activator.CreateInstance:

return (AbstractDto)Activator.CreateInstance 
         (Type.GetType(((DtoSelection)id).ToString(), true, true); 

Oppure, in alternativa, un po 'di trucco, è possibile utilizzare alcuni la generazione di codice per questo:

public static string GenerateValues() 
{ 
    StringBuilder sb = new StringBuilder(); 
    sb.AppendLine("DtoSelection selection = (DtoSelection)id;"); 
    sb.AppendLine("switch (selection)"); 
    foreach (DtoSelection value in (DtoSelection[])Enum.GetValues(typeof(DtoSelection)) 
    { 
     sb.AppendLine("case DtoSelection." + value.ToString() + ":"); 
     sb.AppendLine("return new " + value.ToString() + ";"); 
    } 
} 
0

è necessario utilizzare un contenitore CIO (Unità, StructureMap, Ninject ...)

Un Ioc Consente di:

  • Registrare un tipo con nome, come tale (dipende dal contenitore):

    Container.Register<AbstractDto,Dto1>(DtoSelection.dto1.ToString()); 
    
  • risolvere il tipo

    Container.Resolve<AbstractDto>(DtoSelection.dto1.ToString()); 
    

Questo consente di gestire tutti i dettagli di istanziazione per voi.

Le altre soluzioni offerte sono denominate "IoC di uomo povero". Non reinventare la ruota.

Naturalmente, si dovrebbe nascondere il contenitore dietro metodi:

public void RegisterDto<TDto>(DtoSelection dtoSelection) 
    where TDto : AbstractDto, new() 
    { 
    Container.Register<AbstractDto,Dto1>(dtoSelection.ToString()); 
    } 


    public TDto GetDto<TDto>(DtoSelection dtoSelection) 
    where TDto : AbstractDto 
    { 
    return Container.Resolve<AbstractDto>(dtoSelection.ToString()) as TDto; 
    } 

NOTA: Il new() vincolo (requirement of parameterless constructor) può essere rimosso se si utilizza "costruttore di iniezione". L'iniezione del costruttore consente di registrare i valori che verranno utilizzati come parametri per il costruttore con parametri. Questo parametro può essere altri oggetti o oggetti astratti (interfacce, classi abstrac). Affinché ciò funzioni, è necessario registrare questi parametri nel contianer.

Qualunque IoC scegliate avrà molti vantaggi rispetto al "IoC dei poveri".

UPDATE

Se si vuole evitare di scrivere molte volte, la maggior parte IOC contenitori anche permettono di registrare in base al nome, in modo da poter fare la registrazione in questo modo:

// iterate the DtoSelection Enum 
    foreach(var e in Enum.GetValues(DtoSelection)) 
    { 
    DtoSelection dtoSel = (DtoSelection)e; 
    int n = (int)dtoSel; 
    Container.Register<AbstractDto>("Dto" + n, dtoSel.ToString()); 
    } 

NOTA: Il il primo parametro è il nome del tipo (o il nome completo del tipo). Il secondo è il nome che permetterà di risolverlo.

+1

Questo è eccessivo, IMO. –

+0

@newStackExchangeInstance Perché overkill? Devi semplicemente aggiungere un pacchetto Nuget e scrivere poche righe di codice, meno che se scrivi di possedere "IoC di un povero uomo". Inoltre eviti di commettere errori o di dimenticare dettagli nitidi. Funzionerà in modo più sicuro ed efficiente che praticamente qualsiasi cosa tu possa implementare. Per me il sovradimensionamento sta reinventando la ruota per risolvere un problema già risolto ... e probabilmente introducendo qualche bug inaspettato. – JotaBe

+0

Vediamo, cosa c'è di più complesso (e quindi buggy): Activator.CreateInstance o un complesso framework IoC? –

0
public AbstractDto CreateDto(DtoSelection selection) 
{ 
    return (AbstractDto)Activator.CreateInstance(Type.GetType("Perhaps.Some.Qualifier.Here." + selection.ToString())); 
} 
Problemi correlati