Interruptor o un Diccionario cuando se asigna a un nuevo objeto

12

Recientemente, prefiero mapear relaciones 1-1 usando Dictionaries en lugar de Switch . Creo que es un poco más rápido de escribir y más fácil de procesar mentalmente. Desafortunadamente, cuando se asigna a una nueva instancia de un objeto, no quiero definirlo así:

var fooDict = new Dictionary<int, IBigObject>()
{
    { 0, new Foo() }, // Creates an instance of Foo
    { 1, new Bar() }, // Creates an instance of Bar
    { 2, new Baz() }  // Creates an instance of Baz
}

var quux = fooDict[0]; // quux references Foo

Dada esa construcción, he perdido ciclos de CPU y memoria creando 3 objetos, haciendo lo que sus constructores puedan contener, y solo terminé usando uno de ellos. También creo que la asignación de otros objetos a fooDict[0] en este caso hará que hagan referencia a la misma cosa, en lugar de crear una nueva instancia de Foo según lo previsto. Una solución sería utilizar un lambda en su lugar:

var fooDict = new Dictionary<int, Func<IBigObject>>()
{
    { 0, () => new Foo() }, // Returns a new instance of Foo when invoked
    { 1, () => new Bar() }, // Ditto Bar
    { 2, () => new Baz() }  // Ditto Baz
}

var quux = fooDict[0](); // equivalent to saying 'var quux = new Foo();'

¿Esto está llegando a un punto en el que es demasiado confuso? Es fácil perder ese () al final. ¿O es el mapeo a una función / expresión una práctica bastante común? La alternativa sería usar un interruptor:

IBigObject quux;
switch(someInt)
{
    case 0: quux = new Foo(); break;
    case 1: quux = new Bar(); break;
    case 2: quux = new Baz(); break;
}

¿Qué invocación es más aceptable?

  • Diccionario, para búsquedas más rápidas y menos palabras clave (mayúsculas y minúsculas)
  • Interruptor: más comúnmente encontrado en el código, no requiere el uso de un Func < > objeto para el direccionamiento indirecto.
pregunta KChaloux 18.10.2012 - 16:09

4 respuestas

7

Esa es una versión interesante del patrón de fábrica . Me gusta la combinación del Diccionario y la expresión lambda; me hizo ver ese contenedor de una manera nueva.

Ignoro la preocupación en su pregunta sobre los ciclos de CPU, como mencionó en los comentarios, que el enfoque no lambda no proporciona lo que necesita.

Creo que cualquiera de los dos enfoques (switch vs. Dictionary + lambda) va a estar bien. La única limitación es que al usar el Diccionario, está limitando los tipos de entradas que podría recibir para generar la clase devuelta.

El uso de una instrucción de cambio le proporcionará más flexibilidad en los parámetros de entrada. Sin embargo, si esto tiene que ser un problema, puede ajustar el Diccionario dentro de un método y obtener el mismo resultado final.

Si es nuevo para tu equipo, comenta el código y explica qué está pasando. Solicite una revisión del código del equipo, guíelos a través de lo que se hizo y infórmelos. Aparte de eso, se ve bien.

    
respondido por el GlenH7 18.10.2012 - 16:25
6

C # 4.0 le ofrece la clase Lazy<T> , que es similar a su segunda solución, pero grita "Inicialización perezosa" más explícitamente.

var fooDict = new Dictionary<int, Lazy<IBigObject>>()
{
    { 0, new Lazy(() => new Foo()) }, // Returns a new instance of Foo when invoked
    { 1, new Lazy(() => new Bar()) }, // Ditto Bar
    { 2, new Lazy(() => new Baz()) }  // Ditto Baz
}
    
respondido por el Avner Shahar-Kashtan 20.10.2012 - 13:08
2

Estilísticamente, creo que la legibilidad es igual entre ellos. Es más fácil hacer una inyección de dependencia con el Dictionary .

No olvide que debe verificar si la clave existe cuando se usa Dictionary , y debe proporcionar un respaldo si no es así.

Preferiría la declaración switch para las rutas de código estático y la Dictionary para las rutas de código dinámico (donde puede agregar o eliminar entradas). El compilador podría realizar algunas optimizaciones estáticas con el switch que no puede con el Dictionary .

Curiosamente, este patrón Dictionary es lo que la gente a veces hace en Python, porque Python carece de la declaración switch . De lo contrario, utilizan las cadenas if-else.

    
respondido por el M. Dudley 18.10.2012 - 16:44
1

En general, preferiría que ninguno.

Lo que sea que esté consumiendo debería funcionar con un Func<int, IBigObject> . Entonces, la fuente de su asignación puede ser un Diccionario o un método que tenga una declaración de cambio o una llamada de servicio web o alguna búsqueda de archivos ... lo que sea.

En cuanto a la implementación, prefiero el Diccionario, ya que es más fácil de refaccionar desde 'diccionario de código duro, clave de búsqueda, resultado de retorno' a 'cargar diccionario desde archivo, clave de búsqueda, resultado de retorno'.

    
respondido por el Telastyn 18.10.2012 - 16:26

Lea otras preguntas en las etiquetas