2010-07-12 6 views
6

Así que tengo que diseñar una clase que funcione en una colección de objetos emparejados. Hay una correspondencia de uno a uno entre los objetos. Espero que el cliente de la clase haya establecido esta asignación antes de usar mi clase.¿Cuál es la forma más intuitiva de pedir objetos en pares?

Mi pregunta es ¿cuál es la mejor manera de permitir que el usuario de mi clase me dé esa información?

¿Es para pedir una colección de pares como esta?

MyClass(IEnumerable<KeyValuePair<Object, Object>> objects) 

O separadas colecciones de esta manera?

MyClass(IEnumberable<Object> x, IEnumerable<Object> y) 

¿O hay otra opción?

Me gusta la primera porque la relación es explícita, no me gusta por el trabajo adicional que realiza en el cliente.

Me gusta el segundo porque los tipos son más primitivos y requieren menos trabajo, no me gusta porque el mapeo no es explícito. Debo asumir que el orden es correcto.

Opiniones por favor?

+0

me gusta la primera por la misma razón, la relación es explícito. Dado que KeyValuePair es común en todo el Framework, no veo que exista ningún "trabajo adicional" El único inconveniente que podría tener es que KeyValuePair infiere que T2 está de alguna forma codificado por T1, si la relación entre ellos es más simétrica, puede lanzar su propio tipo (es decir, el par ). – Tergiver

Respuesta

10

En .NET 4 debería utilizar Tuple<T1,T2>. Más información sobre la clase Tuple en MSDN.

+6

.NET 4 solamente: http://msdn.microsoft.com/en-us/library/dd268536.aspx#versionsTitleToggle – Hound

+1

Por el bien del argumento. ¿Por qué usar Tuple en lugar de tu propia clase de emparejamiento? –

+0

@korbinian: eso es lo que pensé, pero MSDN me sorprendió al incluir un enlace a "versiones anteriores" que incluye .NET 3.5. Desafortunadamente, las observaciones allí (que no miré) indican la realidad de que solo es .NET 4. – tvanfosson

1

El primero es mi método preferido, ya que una secuencia puede ser más larga que la otra con la segunda, no mantiene la asignación necesaria 1: 1.

1

En mi opinión, la segunda opción le da a usted y al cliente más trabajo por hacer. La primera opción es más segura y más difícil equivocarse. Escogería la primera opción o algo así siempre.

+0

Sí, eso es lo que dije. –

0

Creo que tienes que ir con la opción 1. Deben existir relaciones explícitas o simplemente estás buscando problemas.

5

Si tiene acceso, use Tuple<T, R>. Si no lo hace, solo escriba una clase genérica de Tuple o Pair. Evitaría usar KeyValuePair, solo porque es detallado y tiene una asociación con Dictionary.

+0

Además, la última vez que revisé, KeyValuePair no se serializa en XML, por lo que esa es otra buena razón para no usarlo si eso es importante para usted. – AaronLS

5

Preferiría KeyValuePair de los dos que menciona, ya que es más expresivo y mantiene la relación entre los objetos.

Pero prefiero crear una clase que tenga una referencia a su par. Esto es más legible en mi opinión, y nunca está de más crear una clase o estructura extra para expresar tus acciones reales.

(pseudo-código)

class MyPair 
{ 
    public TypeA One; 
    public TypeB Two; 
} 

MyClass(IEnumerable<MyPair> objects) 

hay mención de Tuple<,> en algunas respuestas, lo cual es lo más legible KeyValuePair, pero más flexible, ya que puede contener más de dos parámetros.

[Editar - más en profundidad sobre tuplas/clases después de una buena noche de sueño]

To Tuple or Not To Tuple

+0

¿Por qué debería MyPair implementar IEnumerable? –

+0

@Alex, porque no debería :) Tienes razón, y aparentemente necesito dormir. –

+0

No te preocupes por eso afable - Puedo relacionarme con lo del sueño :) –

0

Usaría la segunda versión (porque es más simple) + comentarios + controles estáticos/dinámicos. Si puede usar contratos de código, intente asegurarse de que las colecciones sean de la misma longitud, no nulas. De lo contrario, haga lo mismo con Debug.Assert y if ... throw ArgumentException. Alternativamente, puede crear su propio objeto que contenga el par, pero luego se vuelve más difícil si desea usar genéricos para los miembros del par. Además, cuando crea un objeto que está destinado a almacenarse en un contenedor, debe implementar correctamente GetHashCode, Equals, etc. El primer libro "Effective C#" tiene un elemento sobre este.

Como regla general, prefiero no sobre-diseñar las firmas de métodos.

0

Normalmente proporciono ambos, con el constructor KeyValuePair delegando en el constructor 2-arg. Lo mejor de ambos mundos.

+0

Just curioso - entiendo cómo se puede pasar del método de dos arg al uno de arg haciendo un Zip - ¿cómo se hace al revés? –

+0

public MyClass (KeyValuePair pair): this (pair.Key, pair.Value) {} – benjismith

1

Preferiría la primera. Como dices, la correlación es explícita, y es menos propensa a errores que la segunda donde debes probar que ambas colecciones son de la misma longitud. Por supuesto, puede ser apropiado ofrecer/both/ways, dependiendo de la clase real que esté construyendo.

Una cosa, si usa .NET 4.0, recomendaría usar Tuple<T1, T2> en lugar de KeyValuePair.

0

Me gusta el primer método por dos razones.

  1. si están almacenando sus relaciones en un diccionario <> objeto ya, que sólo puede mano del diccionario como es - sin código adicional requerido.

  2. Si están utilizando su propio almacenamiento distinto entonces dar KeyValuePairs en un IEnumerable es muy fácil con la sentencia yield

Algo como esto:

IEnumerable<KeyValuePair<Object,Object>> GetMappedPairs() 
{ 
    foreach(var pair in _myCustomData) 
    { 
     yield return new KeyValuePair{Key = pair.ID, Value = pair.Data}; 
    } 
} 

opción número 2 no es Los desarrolladores que usan su método lo entienden fácilmente, por lo que requeriría documentación y comentarios para explicar cómo usarlo.

Por cierto, es probable que se sirve mejor al declarar su método como genérico en lugar de codificar duro KeyValuePair

MyClass<TKey,TValue>(IEnumerable<KeyValuePair<TKey,TValue>> pairs); 
0

Puede valer la pena considerar por qué se expone un enumerable en absoluto. Podría usar un método Add (Object a, Object b) que oculte completamente su forma interna de tratar con los pares. El cliente podría configurar sus propios medios para agregarle relaciones como conjuntos o individualmente.

Por supuesto, si todavía necesita un método para tomar un enumerable, entonces la primera opción es probablemente la mejor: una relación explícita. Pero considera usar o hacer un tipo Tuple o Pair para usar en lugar de KeyValuePair, suponiendo que la relación entre ellos no es de naturaleza Key to Value.

0

¿Y si hiciste algo como este?

// The client can choose to put together an IEnumerable<...> by hand...  
public MyClass(IEnumerable<KeyValuePair<object, object>> pairs) 
{ 
    // actual code that does something with the data pairs 
} 

// OR the client can pass whatever the heck he/she wants, as long as 
// some method for selecting the Xs and Ys from the enumerable data is provided. 
// (Sorry about the code mangling, by the way -- just avoiding overflow.) 
public static MyClass Create<T> 
(IEnumerable<T> source, Func<T, object> xSelector, Func<T, object> ySelector) 
{ 
    var pairs = source 
     .Select(
      val => new KeyValuePair<object, object>(
       xSelector(val), 
       ySelector(val) 
      ) 
     ); 

    return new MyClass(pairs); 
} 

Esto permitiría a los clientes escribir código como este:

// totally hypothetical example 
var stockReturns = StockReturns.Create(prices, p => p.Close, p => p.PrevClose); 
Cuestiones relacionadas