2011-03-10 9 views

risposta

10

Vorrei utilizzare un tipo combinato.

Con un oggetto è possibile allegare il comportamento e restituire un oggetto arbitrariamente complesso. Potresti voler ridefinire il tuo metodo in futuro e modificare i valori di ritorno. Riempiendoli in un oggetto di ritorno e aggiungendo un comportamento a quell'oggetto, questo refactoring può diventare in gran parte trasparente.

È allettante usare tuple e simili. Tuttavia lo sforzo di refactoring diventa un mal di testa dopo un po '(sto parlando per esperienza qui, avendo appena fatto questo errore di nuovo)

+0

Anche le tuple sono belle se non vuoi creare un tipo unico ... non abusarne ovviamente ;-) – Amir

6

È consigliabile restituire un tipo combinato. Per lo meno la firma del metodo è più pulita e il programmatore deve fare meno lavoro quando lo chiama. Ad esempio: non devi dichiarare e inizializzare le variabili, che ingombrano il codice chiamante.

+0

Ti dispiacerebbe spiegare perché? –

+0

Vedi modifica recente. –

3

È possibile utilizzare una tupla invece di creare la classe IntersectResult come alternativa.

Riferimento:

http://msdn.microsoft.com/en-us/library/system.tuple.aspx

Tuttavia, mi sarebbe sicuramente propendere verso la restituzione di un tipo complesso sopra fuori parametri.

+0

Grazie, le tuple usano lo stesso tipo come i tipi anon se il tipo esiste già, come Tipo ? –

+1

Credo che fino a quando i tipi che la tupla contiene sono gli stessi, il CLR riutilizzerà il tipo generato. –

1

Ciò dipende interamente da quanto coerente sia il concetto di IntersectResult.

Tecnicamente non c'è motivo di preferire uno rispetto all'altro.

+0

Grazie Henk. Cosa intendi per "quanto è coerente il concetto di IntersectResult", come è probabile che cambi in futuro? –

+0

Se devi definire 'IntersectResult' solo per questo metodo, probabilmente è l'approccio sbagliato. Se 'IntersectResult' è un'idea/classe che viene utilizzata altrove, la versione' out' è ridicola. –

+0

Buon punto Henk. –

5

Alcuni dicono che è una questione di preferenza, ma penso che restituire un oggetto complesso sia meglio non solo per chiarezza, ma anche per manutenzione.

Se è necessario aggiungere un altro bit di dati come output, è necessario modificare la firma del metodo oltre a aggiungere il codice per la risposta aggiuntiva; non così con un tipo di output complesso. Inoltre, è più difficile prendere in giro (per i test di unità) i parametri di output. Sembra anche rompere la filosofia "semplice": devi passare attraverso il problema di impostare i parametri di output quando hai bisogno di un solo pezzo di input. Il punto di forza di un linguaggio OOP è la creazione di tipi: seguire questa strada secondo me.

La differenza di prestazioni tra i due è trascurabile.

2

L'opzione 2 è probabilmente la soluzione migliore se cattura al meglio il tuo dominio. In questo caso il nuovo tipo verrà utilizzato in molti altri luoghi in quanto rappresenta un'entità logica.

3

Solo per la cronaca: E 'difficile trovare un buon esempio del trio magico (chiarezza, semplicità d'uso e prestazioni ).

Ovviamente il secondo esempio fornisce i primi due, mentre il primo fornisce prestazioni migliori. Se questo è trascurabile, non lo so. Forse fai un test di benchmark e scoprilo.

Quello che posso dire è che se si rende il tipo di risultato una struttura di piccole dimensioni, è comunque possibile salvare alcuni punti di prestazione, poiché l'allocazione nell'heap è più costosa rispetto allo stack. Ancora una volta, la copia dei dati da un tipo di valore può superare la penalità di allocazione dell'heap, ma forse non se la si mantiene abbastanza piccola. Inoltre, probabilmente vorrai rendere quel tipo immutabile.

+0

Anche se IntersectResult fosse una struttura, farebbe la differenza? Inoltre, se non intersecasse, il normale sarebbe vuoto proprio come la variabile dichiarata usando il parametro out. Quindi non dovrebbero essere identici? –

+2

Le strutture vengono allocate nello stack e vengono copiate altrove quando necessario. L'allocazione nell'heap può essere sorprendentemente più lenta rispetto alla copia + stack. Inoltre, dal momento che sembra che il tuo tipo contenga solo tipi di valore, rendendolo di per sé un tipo di valore lo farebbe allocare più velocemente rispetto a se assegnassi ciascuno dei suoi membri separatamente. –

+0

Grazie Yam, buoni punti. –

16

avrei usato un tipo combinato e ti dirò perché: perché il calcolo di un valore dovrebbe restituire il valore, non mutare un gruppo di variabili. La modifica di un gruppo di variabili non viene ridimensionata una volta che ne è necessario più di uno modificato. Si supponga di voler un migliaio di queste cose:

IEnumerable<Ray> rays = GetAThousandRays(); 
var intersections = from ray in rays 
        where Intersect(ray, out distance, out normal) 
        orderby distance ... 

L'esecuzione della query è ora più volte mutando le stesse due variabili. Stai ordinando in base a un valore che viene modificato. Questo è un casino. Non fare domande che mutano le cose; è molto confuso.

Quello che vuoi è:

var intersections = from ray in rays 
        let intersection = Intersect(ray) 
        where intersection.Intersects 
        orderby intersection.Distance ... 

No mutazione; manipolare una sequenza di valori come valori non come variabili.

anche io sarei propenso a forse sbarazzarsi di quella bandiera booleano, e rendere il valore di una struct immutabile:

// returns null if there is no intersection 
Intersection? Intersect(Ray ray) { ... } 

struct Intersection 
{ 
    public double Distance { get; private set; } 
    public Vector3 Normal { get; private set; } 
    public Intersection(double distance, Vector3 normal) : this() 
    { 
     this.Normal = normal; 
     this.Distance = distance; 
    } 
} 
+0

Grazie Eric, apprezzo la tua saggezza. Mi libererò del booleano e lo renderò annullabile come hai mostrato, renderà il codice molto chiaro, credo. –

+0

Eric, perché senti che una 'struct' è preferibile a un' class' qui? La mia impressione è che la best practice è qualcosa del tipo: "Inizia con un' class', perché è più facile ragionare, e poi passa a una 'struct' solo se è necessario per motivi di prestazioni" – MgSam

Problemi correlati