2015-08-20 15 views
6

spesso finiscono per scrivere classi come questo:Ridurre costruttore codice piastra della caldaia

public class Animal 
{ 
    public string Colour { get; set; } 
    public int Weight { get; set; } 

    public Animal(Dog data) 
    { 
     this.Colour = data.Colour; 
     this.Weight = data.Weight; 
    } 

    public Animal(Cat data) 
    { 
     this.Colour = data.Colour; 
     this.Weight = data.Weight; 
    } 
} 

Quando si dispone di un sacco di proprietà e tipi poi si finisce in fretta con un sacco di codice refusi. Idealmente in questa situazione vorrei solo creare un'interfaccia IAnimal e fare riferimento a questo. Sono attualmente in una situazione in cui le classi Dog e Cat esistono in un assembly di terze parti e non posso modificarle. L'unica soluzione che posso venire in mente è:

public class Animal 
{ 
    public string Colour { get; set; } 
    public int Weight { get; set; } 

    public Animal(Cat data){Init(data);} 
    public Animal(Dog data){Init(data);} 

    private void Init(dynamic data) 
    { 
     this.Colour = data.Colour; 
     this.Weight = data.Weight; 
    } 
} 

Questo funziona, ma ho perso tutta la sicurezza di tipo, c'è una soluzione migliore rispetto a iniezione costruttore?

Grazie,

Joe

EDIT: Ecco un esempio reale mondo. Ho una libreria di terze parti che restituisce 3 oggetti chiamato:

  • GetPageByIdResult
  • GetPagesByParentIdResult
  • GetPagesByDateResult

(Questi vengono generati tutte auto di classi da un riferimento al servizio e le proprietà sono praticamente identico)

Invece di gestire questi tre oggetti, desidero gestire un singolo oggetto PageData o una raccolta di essi.

+0

Sembra una classe strana, perché Cat e Dog non ereditano da Animal? –

+0

se 'Cane' e' Gatto' ereditano da 'Animale', puoi provare:' Animale pubblico (dati Anima) ' –

+0

Copia Costruttore sull'oggetto base/padre sicuramente non dovrebbe soddisfare tutti i casi dei suoi tipi derivati. Inoltre, se 'Cat' e' Dog' ereditano da 'Animal' puoi fare perfettamente con un singolo costruttore con un parametro' Animal'. – haim770

risposta

3

Si può avere la logica in un costruttore comune che tutti gli altri costruttori chiamano:

public class Animal 
{ 
    public string Colour { get; set; } 
    public int Weight { get; set; } 

    public Animal(Dog data) : this (data.Colour, data.Weight) 
    { 
    } 

    public Animal(Cat data) : this (data.Colour, data.Weight) 
    { 
    } 

    private Animal(string colour, int weight) 
    { 
     this.Colour = colour; 
     this.Weight = weight; 
    } 
} 

Questo è abbastanza simile alla tua seconda soluzione, ma non perde la sicurezza di tipo.

+0

Ah, buona idea. Questa sembra la soluzione migliore finora, ma aspetterò fino a domani prima di accettare. – JoeS

1

Se non si desidera che la classe sia disseminata in questo modo, è possibile provare i metodi di estensione?

public static Animal ToAnimal(this Dog item) 
{ 
    return new Animal() {Weight = item.Weight, Colour = item.Colour}; 
} 

public static Animal ToAnimal(this Cat item) 
{ 
    return new Animal() {Weight = item.Weight, Colour = item.Colour}; 
} 
+0

Puoi anche mostrarne uno generico con un'interfaccia che supporta sia 'Cat' che' Dog', quindi puoi mostrare la vera potenza con i metodi di estensione –

+0

@CallumLinington Egli menziona che sono in un terzo biblioteca di partito però. Quindi, come possiamo farlo? –

+0

Adattatore modello –

3

Sono attualmente in una situazione in cui esistono le Dog e Cat classi in un assembly di terze parti e non riesco a modificare

Io suggerirei soluzione Automapper-based:

public static class AnimalFactory 
{ 
    public static Animal Create<T>(T source) 
     where T : class 
    { 
     Mapper.CreateMap<T, Animal>(); 
     return Mapper.Map<Animal>(source); 
    } 
} 

Usage:

 var catAnimal = AnimalFactory.Create(cat); 
     var dogAnimal = AnimalFactory.Create(dog); 

Naturalmente, è possibile fornire un modo per configurare il mapping personalizzato, se necessario.

+0

Hmm, questo non garantisce la sicurezza del tipo a meno che non si disponga di 1 metodo per tipo. – DavidG

+0

Non solo, ma se ci sono proprietà aggiuntive di cui AutoMapper non è a conoscenza, questo esploderà alle stelle in fase di runtime. Dato che le classi vivono esternamente, ciò potrebbe portare ad una grande quantità di configurazione '.Ignore()', il che rende questo meno elegante. –

+0

@DavidG: è vero. Automapper, sfortunatamente, non ha la possibilità di disattivare la conversione del tipo. Un'altra opzione è lo strumento di mappatura self-made, che potrebbe essere piuttosto difficile da scrivere. – Dennis

0

provare a utilizzare json serializer, con ciò possiamo garantire la sicurezza del tipo.

public class Animal 
    { 
     public string Colour { get; set; } 
     public long Weight { get; set; } 
     public string Name { get; set; } 
     public Animal Create<T>(T anyType) 
     { 
      return GetObject<T, Animal>(anyType); 
     } 
     public K GetObject<T, K>(T type1) 
     { 
      try 
      { 
       var serialized = JsonConvert.SerializeObject(type1); 
       return JsonConvert.DeserializeObject<K>(serialized); 
      } 
      catch (Exception ex) 
      { 
       return default(K); 
      } 
     } 
    } 
    class Program 
    { 
     public static void Main(string[] args) 
     { 
      Animal obj = new Animal(); 
      var animal = obj.Create(new { Colour = "Red", Weight = 100 }); 
      //here you can pass any object, only same name properties will be initialized.. 
      Console.WriteLine(animal.Colour + " : " + animal.Weight); 
      Console.ReadKey(); 
     } 
    } 
Problemi correlati