2009-07-21 17 views
10

¿Debo verificar si una clave particular está presente en el Diccionario si estoy seguro de que se agregará en el diccionario cuando llegue al código para acceder?¿Debo verificar si una determinada clave está presente en el Diccionario antes de acceder a ella?

Hay dos maneras en que puedo acceder al valor en el diccionario de

  1. comprobar método ContainsKey. Si devuelve verdadero, entonces accederé usando indexer [key] del objeto del diccionario.

o

  1. TryGetValue que devolverá verdadero o falso, así como valor de retorno a través de parámetro de salida.

(segundo llevará a cabo mejor que primero si quiero obtener el valor. Benchmark.)

Sin embargo, si estoy seguro de que la función que está accediendo diccionario mundial seguramente tendrá la llave y después ¿Todavía comprobar usando TryGetValue o sin verificar debería usar indexer [].

¿O nunca debería asumir eso y siempre verificar?

Respuesta

18

Utilice el indexador si la clave está presente; si no está presente, lanzará una excepción apropiada, que es el comportamiento correcto si la ausencia de la clave indica un error.

Si es válido para que la llave no esté presente, use TryGetValue y reaccione como corresponda.

(También aplicar los consejos de Marc sobre cómo acceder a un diccionario compartida de manera segura.)

+1

Una clave faltante es a menudo una indicación de otro problema. Si sabe cuál es el problema y puede proporcionar un mensaje más útil, entonces debería considerar lanzar su propia excepción ya que el mensaje que acompaña a 'KeyNotFoundException' es difícil de restringir a veces. –

+0

Sería ciertamente útil si KeyNotFoundException también proporcionara la clave, pero aparte de eso, basta con que el rastro de la pila sea suficiente para mí. ¿Qué tan lejos tomaría esto: verificando todas las referencias nulas posibles (no solo argumentos, sino cosas que internamente espera que sean no nulas) para la nulidad y lanzando una excepción personalizada en cada una de ellas? Siempre habrá excepciones arrojadas por fragmentos del marco en casos de inconsistencia interna: si intenta anticipar cada una de ellas, el código termina siendo más sobre los casos de error que de progreso. –

10

Si el diccionario es global (estático/compartido), debe sincronizar el acceso al mismo (esto es importante; de ​​lo contrario, puede dañarlo).

Incluso si su hilo es solo leyendo datos, debe respetar los bloqueos de otros hilos que podrían estar editándolo.

Sin embargo; si está seguro de que el tema está ahí, el indizador debe estar bien:

Foo foo; 
lock(syncLock) { 
    foo = data[key]; 
} 
// use foo... 

De lo contrario, un modelo útil es para comprobar y añadir en la misma cerradura:

Foo foo; 
lock(syncLock) { 
    if(!data.TryGetValue(key, out foo)) { 
     foo = new Foo(key); 
     data.Add(key, foo); 
    } 
} 
// use foo... 

Aquí sólo añadimos la ítem si no estaba allí ... pero dentro de la misma cerradura.

+0

Bien, pero no creo que esa fuera la pregunta? No veo ninguna referencia a la concurrencia? :) – Thorarin

+0

"diccionario global" - simplemente ponme en una ruta determinada. Si mi entendimiento es incorrecto, OP puede simplemente ignorar esta respuesta ;-p –

+0

¿Por qué tenemos que bloquear cuando leemos datos del diccionario? ¿Es debido a la expansión del diccionario/volver a hash después de un límite? –

2

Siempre revise. Nunca digas nunca. Supongo que su aplicación no es tan crítica para el rendimiento que tendrá que guardar el tiempo de comprobación.

CONSEJO: Si decide no verificar, al menos use Debug.Assert (dict.ContainsKey (key)); Esto solo se compilará cuando se encuentre en el modo de depuración, la compilación de su versión no lo incluirá. De esa forma, al menos podrías tener el cheque al momento de depurar.

Aún así: si es posible, sólo comprobar que :-)

EDIT: Se han producido algunos errores aquí. Con "siempre verificar" no solo me refería a usar un si en alguna parte. Manejar una excepción correctamente también se incluyó en esto. Entonces, para ser más precisos: nunca dé nada por hecho, espere lo inesperado. Verifique con ContainsKey o maneje la posible excepción, pero haga ALGO en caso de que el elemento no esté contenido.

+4

No estoy de acuerdo con esto. Si la clave está destinada a estar presente y no lo está, el comportamiento correcto es arrojar una excepción, ¿sí? Y eso es exactamente lo que hace el indexador, ¿por qué no usar ese comportamiento? Además, no estoy interesado en cambiar el comportamiento entre las compilaciones de depuración y lanzamiento ... me parece una receta de problemas. –

+0

Sí, la verificación de depuración no fue la mejor práctica. PERO es mejor que no verificar en absoluto. Y lanzar una excepción si los datos deben estar en el diccionario es la forma correcta de hacerlo, pero probablemente no sea una excepción interna de Dictionary, sino una que se ajuste mejor al problema real (por ejemplo, InvalidOperationException). – galaktor

+0

Estoy de acuerdo con Jon, solo verifica si puede o no estar allí, si estás seguro de que debería estar allí, entonces la ruta correcta es permitir que se produzca la excepción y manejarla en consecuencia. – James

0

TryGetValue es el mismo código que la indexación por tecla, excepto los antiguos devuelve un valor por defecto (para el parámetro out) cuando éste se emite una excepción. Use TryGetValue y obtendrá revisiones consistentes sin ninguna pérdida de rendimiento.

Editar: Como dijo Jon, si sabes que siempre tendrá la clave, entonces puedes indexarla y dejar que arroje la excepción correspondiente. Sin embargo, si puede proporcionar una mejor información de contexto arrojándola usted mismo con un mensaje detallado, sería preferible.

0

Hay dos líneas de pensamiento sobre esto desde el punto de vista del rendimiento.

1) Evite las excepciones siempre que sea posible, ya que las excepciones son caras, es decir, consulte antes de intentar recuperar una clave específica del diccionario, ya sea que exista o no. Mejor enfoque, en mi opinión, si hay una buena probabilidad de que no exista. Esto evitaría excepciones bastante comunes.

2) Si está seguro de que el artículo existirá allí el 99% del tiempo, entonces no verifique su existencia antes de acceder a él. El 1% de las veces que no existe, se lanzará una excepción pero habrá ahorrado tiempo el otro 99% de las veces al no marcar.

Lo que estoy diciendo es que optimiza para la mayoría si hay una clara. Si existe un grado real de incertidumbre acerca de la existencia de un artículo, entonces verifique antes de recuperarlo.

+0

No creo que esto realmente se deba al rendimiento en absoluto. Debe tratarse de la corrección: si la clave * está * presente * en situaciones correctas, es decir, es un error que la clave no esté allí, entonces lanzar una excepción es el enfoque correcto para el fracaso. De lo contrario, debe detectar esa situación * sin * el uso de una excepción - el manejo de casos no excepcionales con excepciones es una mala idea, IMO. –

+0

Estoy pensando más en el escenario donde no existe en el diccionario no debería dar como resultado una falla, p. debería usar un valor predeterminado en su lugar, o debería agregarlo si no existe. El caso donde debería existir en el diccionario, pero no es un error real si no existe, es decir, supongamos que el diccionario es un caché de algunos datos de un DB, digamos que el DB no está disponible y el caché no se completa, pero no quiere que eso cause una falla en el sistema. Difícil de entender un ejemplo del mundo real, ¡así que espero tener algún tipo de sentido! – AdaTheDev

0

Si sabe que el diccionario normalmente contiene la clave, no tiene que comprobarla antes de acceder a ella.

Si algo no funciona y el diccionario no contiene los elementos que debería, puede dejar que el diccionario arroje la excepción. La única razón para verificar la llave primero sería si usted quiere encargarse de la situación de este problema usted mismo sin obtener la excepción. Dejar que el diccionario arroje la excepción y captar eso es, sin embargo, una forma perfectamente válida de manejar la situación.

0

Creo que Marc y Jon lo tienen (como siempre) bastante sembrado. Como también menciona el rendimiento en su pregunta, puede valer la pena considerar cómo bloquea el diccionario.

La serie lock serializa todo el acceso de lectura que puede no ser deseable si la lectura es muy frecuente y las escrituras son relativamente pocas. En ese caso, usar un ReaderWriterLockSlim podría ser mejor. La desventaja es que el código es un poco más complejo y las escrituras son un poco más lentas.

1

Personalmente verificaría que la clave esté allí, independientemente de si usted está SEGURO o no, algunos pueden decir que este control es superfluo y que el diccionario arrojará una excepción que puede atrapar, pero no debe confiar. en esa excepción, debe verificarse a sí mismo y luego lanzar su propia excepción que significa algo o un objeto resultante con una bandera de éxito y razón dentro ... el mecanismo de falla realmente depende de la implementación.

1

Seguramente la respuesta es "todo depende de la situación".Debe equilibrar el riesgo de que la clave falte en el diccionario (baja para sistemas pequeños donde hay acceso limitado a los datos, donde puede confiar en el orden en que se hacen las cosas, más grande para sistemas más grandes, múltiples programadores que acceden al mismo datos, especialmente con acceso de lectura/escritura/eliminación, donde están involucrados hilos y no se puede garantizar el orden o donde los datos se originan externamente y la lectura puede fallar) con el impacto del riesgo (sistemas críticos para la seguridad, lanzamientos comerciales o sistemas que una empresa confíe en que se lo compara con algo hecho para divertirse, para un trabajo aislado y/o solo para su uso) y con cualquier requisito de velocidad, tamaño y pereza.

Si estuviera haciendo un sistema para controlar la señalización ferroviaria me gustaría estar a salvo de todos los errores posibles e imposibles, y a salvo de errores en el manejo de errores y demás (segunda ley de Murphy: "lo que no puede pasar" el error saldrá mal ".) Si estoy juntando cosas para divertirme, incluso si el tamaño y la velocidad no son un problema, estaré MUCHO más relajado con respecto a cosas como esta: querré llegar a lo divertido.

Por supuesto, a veces esto es lo divertido en sí mismo.

Cuestiones relacionadas