2009-02-05 33 views
29

He estado leyendo cosas malas sobre con la palabra clave en Delphi pero, en mi opinión, si no la usa en exceso. Puede hacer que tu código parezca simple.¿La palabra clave "con" Delphi es una mala práctica?

A menudo pongo todos mis TClientDataSets y TFields en TDataModules. Así que en mis formas tuve código como este

procedure TMyForm.AddButtonClick(Sender: TObject); 
begin 
    with LongNameDataModule do 
    begin 
    LongNameTable1.Insert; 
    LongNameTable1_Field1.Value := "some value"; 
    LongNameTable1_Field2.Value := LongNameTable2_LongNameField1.Value; 
    LongNameTable1_Field3.Value := LongNameTable3_LongNameField1.Value; 
    LongNameTable1_Field4.Value := LongNameTable4_LongNameField1.Value; 
    LongNameTable1.Post; 
    end 
end; 

sin la con de palabras clave que tienen que escribir el código como este

procedure TMyForm.AddButtonClick(Sender: TObject); 
    begin    
     LongNameDataModule.LongNameTable1.Insert; 
     LongNameDataModule.LongNameTable1_LongNameField1.Value := "some value"; 

     LongNameDataModule.LongNameTable1_LongNameField2.Value := 
       LongNameDataModule.LongNameTable2_LongNameField1.Value; 

     LongNameDataModule.LongNameTable1_LongNameField3.Value := 
       LongNameDataModule.LongNameTable3_LongNameField1.Value; 

     LongNameDataModule.LongNameTable1_LongNameField4.Value := 
       LongNameDataModule.LongNameTable4_LongNameField1.Value; 

     LongNameDataModule.LongNameTable1.Post; 
    end; 

Creo que es más fácil de leer usando el con palabra clave.

¿Debo evitar el uso de la palabra clave con?

+0

Aunque estoy de acuerdo con su uso de lo que en este caso - que no sería necesario sin esta forma de utilizar módulos de datos, que está violando la Ley de Demeter. Delphi necesita que todos los componentes en un módulo de formulario/datos sean públicos, pero es mejor diseñar para no hacer (demasiado) uso de eso. – mghie

+1

Duplicado: http://stackoverflow.com/questions/71419/whats-wrong-with-delphis-with –

+0

Se puede utilizar para fines de optimización, para evitar operaciones de desreferencia adicionales, por ejemplo. – arthurprs

Respuesta

22

La declaración with tiene su lugar, pero tengo que aceptar que el uso excesivo puede dar lugar a un código ambiguo. Una buena regla general es asegurarse de que el código sea "más" legible y mantenible después de agregar la declaración con. Si sientes que necesitas agregar comentarios para explicar el código después de agregar el enunciado, entonces probablemente sea una mala idea. Si el código es más legible como en su ejemplo, utilícelo.

por cierto: esto fue siempre uno de mis patrones favoritos en Delphi para mostrar una ventana modal

with TForm.Create(nil) do 
try 
    ShowModal; 
finally 
    Free; 
end 
+1

De acuerdo. Usar "con" es un hábito bueno y malo. Después de un tiempo, aprenderá dónde trazar la línea. – Gerard

+8

Otra solución para formularios modales sería agregar un nuevo método estático "CreateShowModalAndFree";) ... – mjn

+0

Otra buena cosa sobre el uso de la palabra clave 'with' es; puedes escribir 'finalmente gratis' muy a menudo. Es liberador – nurettin

3

El principal problema con "con" es que no se sabe dónde termina su ámbito de aplicación, y se podía tener una superposición múltiple con las declaraciones.

No creo que deba evitar usarlo, siempre que su código sea legible.

Una de las propuestas para que sea más fácil de leer (y menos confusa en el código de tiempo) era si codegear añadió la opciónpara permitir el alias con, y, probablemente, lo que permite múltiples withs en uno:

procedure TMyForm.AddButtonClick(Sender: TObject); 
begin 
    with LongNameDataModule as dm, dm.LongNameTable1 as t1, dm.LongNameTable2 as t2 do 
    begin 
    t1.Insert; 
    t1.FieldByName('Field1').AsString := 'some value'; 
    t1.FieldByName('Field2').AsString := t2.FieldByName('Field2').AsString; 
    t1.Post; 
    dm.Connection.Commit; 
    end 
end; 
+0

En el ejemplo, uso TField en lugar del método FieldByName, por lo que no necesito un alias para LongNameTable2. Solo puedo usar dm.LongTable2_Field1.Value. Gracias por la retroalimentación – Marioh

3

Por lo que a mí respecta, With es bastante aceptable en el caso que das. Ciertamente mejora la claridad del código.

La verdadera maldad es cuando tienes varios contactos abiertos a la vez.

Además, mi opinión es que con lo que está usando la marca hace una gran diferencia. Si se trata de un objeto realmente diferente, entonces el con probablemente sea una mala idea. Sin embargo, no me gusta tener muchas variables en un nivel, incluso cuando esto tiene sentido, generalmente objetos de datos que contienen un elemento de datos muy complejo, generalmente todo el trabajo con el que está diseñado el programa. (No creo que este caso ocurra en una aplicación que no tiene dicho elemento.) Para aclarar el mundo, a menudo uso registros para agrupar elementos relacionados. Encuentro que casi todo lo que uso es para acceder a dichos subgrupos.

8

Cuando comencé la programación pascal (con TurboPascal!) Y aprendí sobre la marcha, CON parecía maravilloso. Como dices, la respuesta al tipado tedioso e ideal para esos largos registros.Desde Delphi llegó, he estado retirarlo y alentando otra para dejarla caer - resumió perfectamente en marcha por Verity en the register Además de una reducción de legibilidad que hay dos razones principales por las que yo evitaría que:

  1. Si usas una clase, entonces no la necesitas de todos modos, solo los registros "parecen" beneficiarse de ella.
  2. El uso del depurador para seguir el código de la declaración con Ctrl-Enter no funciona.

Dicho esto, para facilitar la lectura sigo usando la sintaxis:

procedure ActionOnUpdate(Sender : TObject) 
begin 
    With Sender as TAction do 
    Enabled := Something 
end; 

no he visto una mejor construcción.

13

Tiendo hacia el baning the con-statement por completo. Como se dijo anteriormente, puede complicar las cosas, y mi experiencia es que lo hará. Muchas veces el depurador quiere evaluar los valores debido a los conos, y todos a menudo me encuentro con anidados que conducen a un código que es difícil de leer.

código de Brian parece fácil de leer y agradable, pero el código sería más corto si sólo encasillado al remitente directamente y eliminar toda duda sobre ese componente habilita:

TAction(Sender).Enabled := Something; 

Si usted está preocupado acerca de la escritura de mucho, me prefare hacer una referance temporal al objeto de duración llamado:

var 
    t: TTable; 
begin 
    t := theLongNamedDataModule.WithItsLongNamedTable; 
    t.FieldByName(' '); 
end; 

no puedo por qué se tipificación debería molestarte, sin embargo. We Are Typists First, Programmers Second, y la finalización de código, copiar y pegar y la grabación de clave puede ayudarlo a ser un mecanógrafo más eficaz.

actualización: tropecé sobre un artículo largo con una pequeña sección con declaraciones-: he with keyword. The most hideous, dangerous, blow-your-own-feet-off feature in the language. :-)

+0

Me temo que (para ser pedante) debe ser encasillado como (Remitente como TAcción). Habilitado: = Algo, ya que esto ejecutará el tiempo de ejecución comprobando que el Remitente desciende de TAcción y si no genera un error agradable, un tipografía de rig se bloqueará. –

+0

¿Pero por qué el remitente debe ser algo más que TAction? Si utilizo el mismo manejador de eventos para diferentes tipos de componentes, necesitaré una verificación de qué tipo de remitente generó el evento, pero cuando conecto un evento a un componente específico, ¿por qué debería esperar algo más como remitente? – Vegar

+3

No se trata de tipear, se trata de la claridad del código. Gracias por los comentarios – Marioh

29

El con palabra clave es una buena característica para hacer el código más legible, pero hay algunas trampas .

Depuración:

cuando se utiliza un código como éste:

with TMyClass.Create do 
try 
    Add('foo'); 
finally 
    Free; 
end; 

No hay manera de inspeccionar las propiedades de esta clase, por lo que siempre declarar una variable y utilizar la con palabra clave en eso.

Interfaces:

Al crear una interfaz en la cláusula con vive hasta el final de su método:

procedure MemoryHog; 
begin 
    with GetInterfaceThatTakes50MBOfMemory do 
    Whatever; 
    ShowMessage('I''m still using 50MB of memory!'); 
end; 

Claridad

Cuando usando una clase en una con cláusula que tiene propiedades o nombres de métodos que ya existen dentro del alcance, puede engañarlo fácilmente.

with TMyForm.Create do 
    Width := Width + 2; //which width in this with is width? 

Por supuesto, al tener nombres duplicados, que está utilizando las propiedades y métodos de la clase declarada en su declaración con (TMyForm).

+1

El constructo 'con' confundió al depurador en Delphi 7. No he usado versiones posteriores de Delphi, y no sé si este mal comportamiento ha mejorado –

+1

Al menos hasta BDS2006, no mejora. D2007 y después, no sé. –

+1

Tampoco funciona en D2007. – Pauk

6

Su ejemplo, de un acceso de módulo de datos dentro de un clic de botón, es un ejemplo pobremente ideado en mi opinión. Toda la necesidad de CON desaparece si mueve este código al módulo de datos donde debería estar. El OnClick simplemente llama a LongNameDataModule.InsertStuff y no se necesita.

Con es un dispositivo deficiente, y debe mirar su código para ver por qué lo está necesitando. Probablemente hiciste algo mal, o podrías hacerlo de una mejor manera.

54

El mayor peligro de con, fuera de condiciones patológicas como "con A, B, C, D" es que su código puede cambiar el significado en silencio sin previo aviso. Considere este ejemplo:

with TFoo.Create 
try 
    Bar := Baz; 
    DoSomething(); 
finally 
    Free; 
end; 

cree este código sabiendo que Bar es una propiedad de TFoo, y Baz es una propiedad del tipo que contiene el método que tiene este código.

Ahora, dos años después, algún desarrollador bien intencionado entra agrega una propiedad Baz a TFoo. Tu código silenciosamente ha cambiado el significado. El compilador no se quejará, pero el código ahora está roto.

+14

Brrr ... Ese es realmente terrorífico. –

2

Aquí hay muchas respuestas excelentes acerca de por qué la declaración con está mal, así que intentaré no repetirlas. He estado usando la declaración con durante años y estoy empezando a evitarla. Esto es parcialmente debido a que puede ser difícil calcular el alcance, pero últimamente he comenzado a refactorizar, y ninguna de las refactorizaciones automatizadas funciona con una declaración con, y la refactorización automática es increíble.

También hace algún tiempo hice un vídeo sobre por qué la sentencia with es malo, no es uno de mis mejores trabajos, pero Here it is

3

Soy un firme creyente de la eliminación con el apoyo en Delphi. Su ejemplo de uso de usar un módulo de datos con campos con nombre es la única instancia en la que pude ver que funcionaba. De lo contrario, Craig Stuntz dio el mejor argumento en su contra, que voté a favor.

Me gustaría señalar que con el tiempo eventualmente (debería) controlar todas las codificaciones en eventos OnClick y su código también migrará eventualmente de campos con nombre en módulos de datos a clases que envuelven estos datos y la razón para usar CON se irá.

3

Su pregunta es un excelente ejemplo de 'un martillo no siempre es la solución'.

En este caso, "con" no es su solución: debe trasladar esta lógica comercial de su forma a su módulo de datos. No hacerlo viola Law of Demeter como mghie (Michael Hieke) ya comentado.

Tal vez su ejemplo fue sólo ilustrativa, pero si en realidad se está utilizando código como el que en sus proyectos, esto es lo que debe hacer en lugar:

procedure TLongNameDataModule.AddToLongNameTable1(const NewField1Value: string); 
begin 
    LongNameTable1.Insert; 
    LongNameTable1_Field1.Value := NewField1Value; 
    LongNameTable1_Field2.Value := LongNameTable2_LongNameField1.Value; 
    LongNameTable1_Field3.Value := LongNameTable3_LongNameField1.Value; 
    LongNameTable1_Field4.Value := LongNameTable4_LongNameField1.Value; 
    LongNameTable1.Post; 
end; 

Y luego llamarlo desde el formulario de la siguiente manera:

procedure TMyForm.AddButtonClick(Sender: TObject); 
begin 
    LongNameDataModule.AddToLongNameTable1('some value'); 
end; 

Esto efectivamente elimina su declaración y hace que su código sea más fácil de mantener al mismo tiempo.

Por supuesto que rodea cadenas de Delphi con comillas simples ayudará a compilar lo que es, así ;-)

+2

Estaba a punto de escribir exactamente lo mismo, ahora que leo esta respuesta ya no necesito hacerlo. El ejemplo fue infeliz, es una clara violación de la Ley de Demeter. Pero el polémico arround 'with' va más allá de esto, con otros argumentos –

5

Como se mencionó Vegar, que es tan limpio y mucho más fácil de leer, fácil de depurar, y menos propensos a problemas de sigilo para usa una referencia temporal

Hasta el momento, no tengo nunca se encontró la necesidad de utilizar con . Solía ​​ser ambivalente, hasta que me hice cargo de un proyecto que usaba la mente doblando doble con con frecuencia. Cuestionando si el desarrollador original tenía la intención de referenciar elementos en el primer con o en el segundo, si esa referencia ambigua era con deslizamiento o código torpe, el tormento de intentar depurarlo y los efectos de extensión o modificación las clases que usan estas abominaciones simplemente no valen el tiempo de nadie.

El código explícito es simplemente más legible. De esta forma puedes tener tu pastel y disfrutar comiendo.

procedure TMyForm.AddButtonClick(Sender: TObject); 
var 
    dm: TLongNameDataModuleType 
begin 
    dm:=LongNameDataModule; 

    dm.LongNameTable1.Insert; 
    dm.LongNameTable1_Field1.Value := "some value"; 
    dm.LongNameTable1_Field2.Value := LongNameTable2_LongNameField1.Value; 
    dm.LongNameTable1_Field3.Value := LongNameTable3_LongNameField1.Value; 
    dm.LongNameTable1_Field4.Value := LongNameTable4_LongNameField1.Value; 
    dm.LongNameTable1.Post; 
end; 
+1

Double with is evil. –

0

la corriente con la declaración es "peligroso", pero se puede mejorar sustancialmente:

With TForm1.Create (Nil) Do // New TForm1 instance 
    Try 
     LogForm ("); // That same instance as parameter to an outer method 
     "ShowModal; // Instance.ShowModal 
    Finally 
     "Free; // Instance.Free 
    End; 

Mi propuesta es:

  1. No más de un objeto/registro por Con cabecera .
  2. Anidado No permitido.
  3. El uso de "para indicar el objeto/registro (comillas dobles son similares hasta la marca de ídem: http://en.wikipedia.org/wiki/Ditto_mark).
Cuestiones relacionadas