Prévia do material em texto
lndice . . Agradecimientos ....................................................................................................................... 6 Contactar con el autor .............................................................................................................. 7 Siete versiones y contando ..................................................................................................... 29 La estructura del libro ............................................................................................................ 31 Normas usadas en este libro ............................................................................................ 32 Parte I . Bases ......................................................................................................................... 33 1 . Delphi 7 y su IDE .............................................................................................................. 35 Ediciones de Delphi ................................................................................................................ 36 Una vision global del IDE ..................................................................................................... 37 Un IDE para dos bibliotecas ............................................................................................ 38 . . Configuration del escritorio ..................................................................................... 38 Environment Options ....................................................................................................... 40 Sobre 10s menus ................................................................................................................. 40 El cuadro de dialog0 Environment Options .............................................................. 41 TO-DO List .......................................................................................................................... 41 Mensajes ampliados del compilador y resultados de busqueda en Delphi 7 .............. 43 El editor de Delphi ................................................................................................................. 44 El Code Explorer ............................................................................................................... 46 . . Exploracion en el editor ................................................................................................... 48 Class Completion .............................................................................................................. 49 Code Insight ...................................................................................................................... 50 Code Completion ......................................................................................................... 50 Code Templates ............................................................................................................ 52 Code Parameters .......................................................................................................... 52 Tooltip Expression Evaluation ................................................................................... 53 Mas teclas de metodo abreviado del editor .................................................................... 53 Vistas que se pueden cargar ............................................................................................. 54 .............................................................................................................. Diagram View 54 Form Designer ......................................................................................................................... 56 ................................................................................................................ Object Inspector 58 Categorias de propiedades .......................................................................................... 60 Object TreeView ................................................................................................................ 61 Secretos de la Component Palette ......................................................................................... 63 Copiar y pegar componentes ............................................................................................ 64 De las plantillas de componentes a 10s marcos ............................................................. 65 Gestionar proyectos ................................................................................................................ 67 Opciones de proyecto ........................................................................................................ 69 .............................................................................................. Compilar y crear proyectos 71 Ayudante para mensajes del compilador y advertencias ......................................... 73 Exploracion de las clases de un proyecto ....................................................................... 74 Herramientas Delphi adicionales y externas .............................. .......... .............................. 75 Los archivos creados por el sistema ..................................................................................... 76 Un vistazo a 10s archivos de codigo fuente .................................................................... 82 El Object Repository ............................................................................................................... 84 Actualizaciones del depurador en Delphi 7 ......................................................................... 87 2 . El lenguaje de programaci6n Delphi .......................................................................... 89 Caracteristicas centrales del lenguaje .................................................................................. 90 Clases y objetos ....................................................................................................................... 91 Mas sobre metodos ............................................................................................................ 93 Creacion de componentes de forma dinamica ............................................................ 94 Encapsulado ............................................................................................................................ 95 ............................................................................................ Privado, protegido y public0 96 Encapsulado con propiedades ......................................................................................... 97 Propiedades de la clase TDate ................................................................................... 99 Caracteristicas avanzadas de las propiedades ........................................................ 100 Encapsulado y formularios ............................................................................................. 101 ........................................................................... Aiiadir propiedades a formularios 101 ......................................................................................................................... Constructores 103 ....................................................................................... Destructores y el metodo Free 104 El modelo de referencia a objetos de Delphi ..................................................................... 104 Asignacion de objetos ..................................................................................................... 105 Objetos y memoria ........................................................................................................... 107 ................................................................................... Destruir objetos una sola vez 108 ..........................................................................................Herencia de 10s tipos existentes 109 ................................................................................ Campos protegidos y encapsulado 111 ............................................................................... Herencia y compatibilidad de tipos 113 ......................................................................................... Enlace posterior y polimorfismo 114 ................................................................................... Sobrescribir y redefinir metodos 11 5 Metodos virtuales frente a metodos dinamicos ............................................................ 117 ......................................................................................... Manejadores de mensajes 117 .......................................................................................................... Metodos abstractos 118 Conversion descendiente con seguridad de tipos .............................................................. 119 ................................................................................................................... Uso de interfaces 121 ..................................................................................................... Trabajar con excepciones 124 ........................................................................... Flujo de programa y el bloque finally 125 ..................................................................................................... Clases de excepciones 127 .......................................................................................................... Registro de errores 129 .............................................................................................................. Referencias de clase 130 Crear . componentes usando referencias de clase ....................................................... 132 . . 3 . La biblioteca en tiempo de ejecuc~on ......................................................................... 135 ........................................................................................................ . Las unidades de la RTL 136 ...................................................................................... Las unidades System y SysInit 139 Cambios recientes en la unidad System .................................................................. 140 Las unidades SysUtils y SysConst ................................................................................. 141 .................................................................................. Nuevas hnciones de SysUtils 142 ........................................ Rutinas extendidas de formato de cadenas en Delphi 7 144 La unidad Math ............................................................................................................... 145 Nuevas funciones matematicas ................................................................................ 145 .................................................................................. Redondeo y dolores de cabeza 147 Las unidades ConvUtils y StdConvs ............................................................................. 148 La unidad DateUtils ........................................................................................................ 148 La unidad StrUtils ........................................................................................................... 149 De Pos a PosEx .......................................................................................................... 150 .............................................................................................................. La unidad Types 151 ....................................................................................... La unidad Variants y VarUtils 151 Variantes personalizadas y numeros complejos ..................................................... 152 Las unidades DelphiMM y ShareMem ......................................................................... 154 Unidades relacionadas con COM .................................................................................. 154 ...................................................................................................................... Convertir datos 154 ................................................................................................... iConversiones de divisas? 158 ........................................................................................ Gestion de archivos con SysUtils 162 La clase TObject ................................................................................................................... 163 Mostrar information de clase ........................................................................................ 167 4 . La biblioteca de clases principales ............................................................................. 169 .............................................................................................. El paquete RTL. VCL y CLX 170 ...................................................................................... Partes tradicionales de la VCL 171 La estructura de CLX ..................................................................................................... 172 Partes especificas de VCL de la biblioteca ................................................................... 173 .............................................................................................................. La clase TPersistent 173 La palabra clave published ............................................................................................. 176 Acceso a las propiedades por su nombre ...................................................................... 177 La clase TComponent ........................................................................................................... 180 ............................................................................................................................ Posesion 180 La matriz Components ......................................................................................... 181 .............................................................................................. Cambio de propietario 182 ......................................................................................................... La propiedad Name 184 . . Elimination de campos del formulario ......................................................................... 185 ...................................................................................... Ocultar campos del formulario 186 .................................................................................... La propiedad personalizada Tag 188 ...................................................................................................... ............................ Eventos : 188 Eventos en Delphi ........................................................................................................... 188 Punteros a metodo ....................................................................................................... 189 Los eventos son propiedades ......................................................................................... 190 Listas y clases contenedores ............................................................................................... 193 Listas y listas de cadena ............................................................................................... 193 ................................................... Pares nombre-valor (y extensiones de Delphi 7) 194 Usar listas de objetos ................................................................................................. 195 ...................................................................................................................... Colecciones 196 Clases de contenedores............................................................................................. 196 . . . . ............................................................................ Listas asociativas de verification 198 Contenedores y listas con seguridad de tipos .......................................................... 199 Streaming ............................................................................................................................... 202 La clase TStream ............................................................................................................. 202 ......................................................................................... Clases especificas de streams 204 .............................................................................................. Uso de streams de archivo 205 ........................................................................................ Las clases TReader y TWriter 206 Streams y permanencia ................................................................................................... 207 .................................................................................. Compresion de streams con ZLib 213 ................... Resumen sobre las unidades principales de la VCL y la unidad BaseCLX 215 ........................................................................................................... La unidad Classes 215 .............................................................................. Novedades en la unidad Classes 216 . . ............................................................................................. Otras unidades prlncipales 217 5 . Controles visuales ........................................................................................................... 219 ...................................................................................................... VCL frente a VisualCLX 220 .......................................................................... Soporte dual de bibliotecas en Delphi 222 ......................................................................... Clases iguales, unidades diferentes 223 DFM y XFM ............................................................................................................... 224 .......................................................................................................... Sentencias uses 226 ............................................... Inhabilitar el soporte de ayuda a la biblioteca dual 226 Eleccion de una biblioteca visual .................................................................................. 226 ................................................................................................... Ejecucion en Linux 227 ........................................................... Compilacion condicional de las bibliotecas 228 .......................................................................... Conversion de aplicaciones existentes 229 ......................................................................................... Las clases TControl y derivadas 230 Parent y Controls ............................................................................................................ 231 Propiedades relacionadas con el tamafio y la posicion del control ........................... 232 ........................................................................ Propiedades de activation y visibilidad 232 Fuentes ............................................................................................................................. 233 Colores ............................................................................................................................. 233 La clase TWinControl (VCL) ........................................................................................ 235 ................................................................................... La clase TWidgetControl (CLX) 236 ................................................................. Abrir la caja de herramientas de componentes 236 Los componentes de entrada de texto ........................................................................... 237 El componente Edit ................................................................................................... 237 El control LabeledEdit ............................................................................................. 238 El componente MaskEdit .......................................................................................... 238 Los componentes Memo y RichEdit ........................................................................ 239 ...................................................................................... El control CLX Textviewer 240 Seleccion de opciones ..................................................................................................... 240 Los componentes CheckBox y RadioButton ........................................................... 241 Los componentes GroupBox ..................................................................................... 241 El componente RadioGroup ..................................................................................... 241 Listas ................. ' ............................................................................................................... 242 El componente ListBox ............................................................................................. 242 El componente ComboBox ....................................................................................... 243 El componente CheckListBox .................................................................................. 244 Los cuadros combinados extendidos: ComboBoxEx y ColorBox ......................... 245 Los componentes Listview y TreeView .................................................................. 246 El componente ValueListEditor ............................................................................... 246 Rangos .............................................................................................................................. 248 El componente ScrollBar .......................................................................................... 248 Los componentes TrackBar y ProgressBar ............................................................. 249 El componente UpDown ........................................................................................... 249 El componente PageScroller .................................................................................... 249 El componente ScrollBox ......................................................................................... 250 Comandos ......................................................................................................................... 250 Comandos y acciones ................................................................................................ 251 Menu Designer .......................................................................................................... 251 Menus contextuales y el evento OncontextPopup .............................................. 252 Tecnicas relacionadas con 10s controles ............................................................................ 254 Gestion del foco de entrada ............................................................................................ 254 Anclajes de control ......................................................................................................... 257 Uso del componente Splitter .......................................................................................... 258 Division en sentido horizontal ................................................................................. 260 Teclas aceleradoras .........................................................................................................261 Sugerencias flotantes ...................................................................................................... 262 .......................................................................... Personalizacion de las sugerencias 263 Estilos y controles dibujados por el propietario .......................................................... 264 Elementos del menu dibujados por el usuario ........................................................ 265 Una ListBox de colores ............................................................................................. 267 Controles ListView y TreeView ........................................................................................... 270 ..................................................................................... Una lista de referencias grafica 270 Un arb01 de datos ............................................................................................................ 275 La version adaptada de DragTree ............................................................................ 278 Nodos de arb01 personalizados ...................................................................................... 280 . . 6 . Creac~on de la interfaz de usuario .............................................................................. 283 Formularios de varias paginas ............................................................................................ 284 Pagecontrols y Tabsheets .............................................................................................. 285 Un visor de imagenes con solapas dibujadas por el propietario ................................ 290 La interfaz de usuario de un asistente .......................................................................... 294 El control ToolBar ................................................................................................................ 297 El ejemplo RichBar ......................................................................................................... 298 Un menu y un cuadro combinado en una barra de herramientas .............................. 300 Una barra de estado simple ............................................................................................ 301 Temas y estilos ...................................................................................................................... 304 Estilos CLX ..................................................................................................................... 305 ................................................................................................... Temas de Windows XP 305 El Componente ActionList .................................................................................................. 308 Acciones predefinidas en Delphi ................................................................................... 310 Las acciones en la practica ............................................................................................ 312 La barra de herramientas y la lista de acciones de un editor ..................................... 316 Los contenedores de barra de herramientas ....................................................................... 318 ....................................................................................................................... ControlBar 320 Un menu en una barra de control ............................................................................ 323 Soporte de anclaje en Delphi ......................................................................................... 323 Anclaje de barras de herramientas en barras de control ............................................ 324 ..................................................................... Control de las operaciones de anclaje 325 Anclaje a un Pagecontrol .............................................................................................. 329 ..................................................................................... La arquitectura de ActionManager 331 Construir una sencilla demostracion ............................................................................ 332 ..................................................... Objetos del menu utilizados con menos frecuencia 336 Modificar un programa existente .................................................................................. 339 Emplear las acciones de las listas ................................................................................. 340 7 . Trabajo con formularios ................................................................................................ 345 La clase TForm ..................................................................................................................... 346 ............................................................................................. Usar formularios normales 346 El estilo del formulario .................................................................................................. 348 El estilo del borde ........................................................................................................... 349 ....................................................................................................... Los iconos del borde 352 Definicion de mas estilos de ventana ............................................................................ 354 Entrada directa en un formulario ........................................................................................ 356 Supervision de la entrada del teclado ........................................................................... 356 Obtener una entrada de raton ........................................................................................ 358 Los parametros de 10s eventos de raton ............................................................... 359 Arrastrar y dibujar con el raton ..................................................................................... 359 Pintar sobre formularios ...................................................................................................... 364 Tecnicas inusuales: Canal Alpha, Color Key y la API Animate ..................................... 366 Posicion, tamaiio, desplazamiento y ajuste de escala ....................................................... 367 . . ............................................................................................. La posicion del formulario 368 ................................................................................. Ajuste a la ventana (en Delphi 7) 368 ........................................................ El tamafio de un formulario y su zona de cliente 369 Restricciones del formulario .......................................................................................... 370 ................................................................................................ Desplazar un formulario 370 Un ejemplo de prueba de desplazamiento ............................................................... 371 Desplazamiento automatico ..................................................................................... 373 ...................................................... Desplazamiento y coordenadas del formulario 374 ................................................................................................. Escalado de formularios 376 Escalado manual del formulario .............................................................................. 377 ............................................................. Ajuste automatic0 de la escala del formulario 378 Crear y cerrar formularios ................................................................................................... 379 Eventos de creacion de formularios .............................................................................. 381 ......................................................................................................Cerrar un formulario 382 ........................................................ Cuadros de dialog0 y otros formularios secundarios 383 Afiadir un formulario secundario a un programa ........................................................ 384 Crear formularios secundarios en tiempo de ejecucion .............................................. 385 ........................................... Crear un unica instancia de formularios secundarios 386 . . Creacion de un cuadro de d~alogo ....................................................................................... 387 El cuadro de dialogo del ejemplo RefList .................................................................... 388 ..................................................................................... Un cuadro de dialog0 no modal 390 . . Cuadros de dialog0 predefinidos ......................................................................................... 393 Dialogos comunes de Windows ................................................................................... 394 Un desfile de cuadros de mensaje ................................................................................. 395 Cuadros "Acerca den y pantallas iniciales ......................................................................... 396 . , Creacion de una pantalla inicial ................................................................................... 397 Parte I1 . Arquitecturas orientadas a objetos en Delphi ............................................... 401 ............................................................... 8 . La arquitectura de las aplicaciones Delphi 403 El objeto Application ............................................................................................................ 404 . . Mostrar la ventana de la aplicacion .............................................................................. 406 Activacion de aplicaciones y formularios .................................................................... 407 Seguimiento de formularios con el objeto Screen ..................................................... 407 De eventos a hilos ................................................................................................................. 412 Programacion guiada por eventos ................................................................................. 412 Entrega de mensajes Windows ...................................................................................... 414 .................................................................................... Proceso secundario y multitarea 414 Multihilo en Delphi ........................................................................................................ 415 Un ejemplo con hilos ................................................................................................ 416 Verificando si existe una instancia previa de una aplicacion .......................................... 418 ................................................................ Buscando una copia de la ventana principal 418 Uso de un mutex .............................................................................................................. 419 Buscar en una lista de ventanas .................................................................................... 420 Controlar mensajes de ventana definidos por el usuario ............................................ 421 Creacion de aplicaciones MDI ............................................................................................ 422 MDI en Windows: resumen tecnico ............................................................................. 422 Ventanas marco y ventanas hijo en Delphi ........................................................................ 423 Crear un menu Window completo ................................................................................. 424 El ejemplo MdiDemo ...................................................................................................... 426 Aplicaciones MDI con distintas ventanas hijo .................................................................. 428 Formularios hijo y mezcla de menus ............................................................................ 428 ................................................................................................... El formulario principal 429 Subclasificacion de la ventana MdiClient .................................................................... 430 Herencia de formularios visuales ........................................................................................ 432 Herencia de un formulario base .................................................................................... 4 3 3 Formularios polimorficos ............................................................................................... 436 Entender 10s marcos ............................................................................................................. 439 Marcos y fichas ............................................................................................................... 442 ................................................................................................. Varios marcos sin fichas 444 Formularios base e interfaces .............................................................................................. 446 Uso de una clase de formulario base ............................................................................. 447 ............................................................ Un truco adicional: clases de interposition 450 Uso de interfaces ............................................................................................................. 451 El gestor de memoria de Delphi .......................................................................................... 452 . . 9 . Creac~on de componentes Delphi ................................................................................. 455 Ampliacion de la biblioteca de Delphi ............................................................................... 456 .............................................................................................. Paquetes de componentes 4 5 6 Normas para escribir componentes ............................................................................... 458 Las clases basicas de componentes ............................................................................... 459 . . Creacion de nuestro primer componente ........................................................................... 460 ............................................................................................ El cuadro combinado Fonts 460 Creacion de un paquete .................................................................................................. 465 ............................................................................... ~QuC hay detras de un paquete? 466 Uso del cuadro combinado Fonts ................................................................................... 469 ................................................................. Los mapas de bits de la Component Palette 469 Creacion de componentes compuestos ............................................................................... 471 .................................................................................................... Componentes internos 471 Publicacion de subcomponentes .................................................................................... 472 .................................................................................................... Componentes externos 475 .......................................................... Referencias a componentes mediante interfaces 477 Un componente grafico complejo ........................................................................................ 481 ......................................................................Definition de una propiedad enumerada 482 Escritura del metodo Paint ............................................................................................. 484 Adicion de las propiedades TPersistent ........................................................................ 486 Definition de un nuevo evento personalizado ............................................................. 488 ..... Uso de llamadas de bajo nivel a la API de Windows : ....................................... 489 La version CLX: Llamadas a funciones Qt nativas ............................................... 490 Registro de las categorias de propiedades .................................................................... 490 Personalizacion de 10s controles de Windows ................................................................... 492 El cuadro de edicion numeric0 ...................................................................................... 494 Un editor numeric0 con separador de millares ...................................................... 495 El boton Sound ................................................................................................................ 496 Control de mensaje internos: El boton Active ........................................................... 498 Mensajes de componente y notificaciones .................................................................... 499 Mensajes de componentes ........................................................................................ 499 Notificaciones a componentes .................................................................................. 503 ................................................................. Un ejemplo de mensajes de componente 503 Un cuadro de dialog0 en un componente ........................................................................... 504 Uso del componente no visual ....................................................................................... 508 Propiedades de coleccion ............................................................................................... 508 Definicion de acciones personalizadas ........................................................................ 512 Escritura de editores de propiedades .................................................................................. 516 Un editor para las propiedades de sonido ................................................................ 517 Instalacion del editor de propiedades ..................................................................... 520 ............................................................................... Creacion de un editor de componentes 521 ......................................................... Subclasificacion de la clase TComponentEditor 522 .................................................................. Un editor de componentes para ListDialog 522 Registro del editor de componentes ........................................................................ 524 10 . Bibliotecas y paquetes ................................................................................................. 527 La funcion de las DLL en Windows ............................................................................ 528 El enlace dinamico .......................................................................................................... 528 Uso de las DLL ................................................................................................................ 529 Normas de creacion de DLL en Delphi ....................................................................... 530 Uso de las DLL existentes .................................................................................................... 531 .................................................................................................... Usar una DLL de C++ 532 Creacion de una DLL en Delphi ......................................................................................... 534 La primera DLL en Delphi ...................................................................................... 535 Funciones sobrecargadas en las DLL de Delphi ................................................... 537 Exportar cadenas de una DLL ................................................................................. 537 Llamada a la DLL de Delphi ...................................................................................... 539 Caracteristicas avanzadas de las DLL en Delphi ............................................................ 540 ............................................................. Cambiar nombres de proyecto y de biblioteca 540 Llamada a una funcion DLL en tiempo de ejecucion .................................................. 542 Un formulario de Delphi en una DLL .......................................................................... 544 Bibliotecas en memoria: codigo y datos ............................................................................. 546 Compartir datos con archivos proyectados en memoria ............................................. 548 Uso de paquetes Delphi ........................................................................................................ 550 Versiones de paquetes .................................................................................................... 551 Formularios dentro de paquetes .......................................................................................... 553 Carga de paquetes en tiempo de ejecucion ................................................................ 555 Uso de interfaces en paquetes .................................................................................. 558 Estructura de un paquete ..................................................................................................... 561 11 . Modelado y programacih orientada a objetos (con ModelMaker) ................... 567 Comprension del modelo interno de ModelMaker ............................................................ 568 Modelado y UML .................................................................................................................. 569 Diagramas de clase ........................................................................................................ 569 Diagramas de secuencia ............................................................................................. 571 Casos de uso y otros diagramas ..................................................................................... 572 Diagramas no W ........................................................................................................ 574 Elementos comunes de 10s diagramas ........................................................................... 575 Caracteristicas de codification de ModelMaker .......................................................... 576 ................................................................................. Integracion Delphi / ModelMaker 576 Gestion del modelo de codigo ........................................................................................ 578 El editor Unit Code Editor ............................................................................................. 580 ........................................................... El editor Method Implementation Code Editor 582 La vista de diferencias .................................................................................................... 582 La vista Event Types View ............................................................................................. 584 ..................................................................................................... Documentacion y macros 585 Documentacion frente a comentarios ............................................................................ 585 Trabajo con macros .........................................................................................................587 Reingenieria de codigo ......................................................................................................... 587 . . Aplicacion de patrones de diseiio .................................................................................. 590 Plantillas de codigo ......................................................................................................... 593 Detallitos poco conocidos ................................................................................................... 595 12 . De COM a COM+ ..................................................................................................... 597 Una breve historia de OLE y COM ..................................................................................... 598 Implementacion de IUnknow .............................................................................................. 599 ............................................................................... Identificadores globalmente unicos 601 El papel de las fabricas de clases .................................................................................. 603 Un primer sewidor COM ..................................................................................................... 604 Interfaces y objetos COM ............................................................................................... 605 Inicializacion del objeto COM ....................................................................................... 608 Prueba del sewidor COM ............................................................................................... 609 Uso de las propiedades de la interfaz ........................................................................... 610 ......................................................................................... Llamada a metodos virtuales 611 ..................................................................................................................... Automatization 612 Envio de una llamada Automatizacion ......................................................................... 614 Creacion de un sewidor de Automatizacion ...................................................................... 617 .................................................................................... El editor de bibliotecas de tipos 618 ..................................................................................................... El codigo del sewidor 619 Registro del sewidor de autornatizacion ...................................................................... 621 Creacion de un cliente para el sewidor ........................................................................ 622 ................................................................ El alcance de 10s objetos de automatizacion 624 El senidor en un componente ...................................................................................... 626 Tipos de datos COM ....................................................................................................... 627 Exponer listas de cadenas y fuentes ....................................................................... 627 Us0 de programas Office ................................................................................................ 628 Uso de documentos compuestos .......................................................................................... 629 ............................................................................................... El componente Container 630 Uso del objeto interno .................................................................................................... 633 Controles ActiveX ................................................................................................................. 633 ........................................................ Controles ActiveX frente a componentes Delphi 635 Uso de controles ActiveX en Delphi ............................................................................. 636 Uso del control WebBrowser .................................................................................... 636 Creacion de controles ActiveX ............................................................................................ 638 Creacion de una flecha ActiveX .................................................................................... 639 Afiadir Nuevas Propiedades ........................................................................................... 640 Adicibn de una ficha de propiedades ............................................................................ 642 ActiveForms ..................................................................................................................... 644 Interioridades de ActiveForm ................................................................................... 644 El control ActiveX XClock ...................................................................................... 645 ActiveX en paginas Web ................................................................................................ 646 COM+ .................................................................................................................................... 648 Creacion de un componente COM+ .............................................................................. 649 Modulos de datos transaccionales ................................................................................. 651 Eventos COM+ ................................................................................................................ 653 COM y .NET en Delphi 7 .................................................................................................... 656 ................................ Parte I11 . Arquitecturas orientadas a bases de datos en Delphi 659 13 . Arquitectura de bases de datos Delphi ..................................................................... 661 Acceso a bases de datos: dbExpress. datos locales y otras alternativas .......................... 662 La biblioteca dbExpress .................................................................................................. 662 Borland Database Engine (BDE) .................................................................................. 664 InterBase Express (IBX) ................................................................................................ 664 MyBase y el componente ClientDataSet ....................................................................... 665 dbGo para ADO ............................................................................................................... 665 MyBase: ClientDataSet independiente ............................................................................... 666 Conexion a una tabla local ya existente ....................................................................... 667 De la DLL Midas a la unidad MidasLib ....................................................................... 669 Formatos XML y CDS .................................................................................................... 669 Definition de una tabla local nueva .............................................................................. 670 Indexado ........................................................................................................................... 671 Filtrado ............................................................................................................................. 672 Busqueda de registros ..................................................................................................... 673 Deshacer y Savepoint ................................................................................................ 674 Activar y desactivar el registro ................................................................................ 675 Usode controles data-aware ................................................................................................ 675 Datos en una cuadricula ................................................................................................. 676 DBNavigator y acciones sobre el conjunto de datos ................................................... 676 Controles data-aware de texto ....................................................................................... 677 Controles data-aware de lista ........................................................................................ 677 El ejemplo DbAware ................................................................................................. 678 Uso de controles de busqueda ........................................................................................ 679 Controles grAficos data-aware ....................................................................................... 681 El componente DataSet ........................................................................................................ 681 El estado de un Dataset .................................................................................................. 686 Los campos de un conjunto de datos .................................................................................. 687 Uso de objetos de campo ................................................................................................ 690 Una jerarquia de clases de campo ................................................................................. 692 ., Adicion de un campo calculado ..................................................................................... 695 Campos de busqueda ....................................................................................................... 699 Control de 10s valores nulos con eventos de campo .................................................... 701 Navegacion por un conjunto de datos ................................................................................. 702 El total de una columna de tabla ................................................................................... 703 Uso de marcadores .......................................................................................................... 704 Edicion de una columna de tabla ................................................................................. 707 Personalizacion de la cuadricula de una base de datos .................................................... 707 Pintar una DBGrid ...................................................................................................... 708 Una cuadricula que permite la seleccion multiple ................................................. 710 ........................................................................................ Arrastre sobre una cuadricula 712 Aplicaciones de bases de datos con controles estandar .................................................... 713 Imitacion de 10s controles data-aware de Delphi ....................................................... 713 Envio de solicitudes a la base de datos ......................................................................... 716 ....................................................................................................... Agrupacion y agregados 718 ...................................................................................................................... Agrupacion 718 ................................................................................................. Definicion de agregados 719 Estructuras maestroldetalles ................................................................................................ 721 Maestro/detalle con 10s ClientDataSet ..................................................................... 722 Control de errores de la base de datos ............................................................................ 723 14 . Clientelsemidor con dbExpress ............................................................................ 727 La arquitectura clientelservidor .......................................................................................... 728 Elementos del disefio de bases de datos .......................................................................... 730 Entidades y relaciones .................................................................................................... 730 Reglas de normalizacion ........................................................................................ 731 De las claves primarias a 10s OID ................................................................................. 731 ................................................................. Claves externas e integridad referencial 733 . . Mas restricciones ............................................................................................................ 734 Cursores unidireccianales ....................................................................................... 734 ...................................................................................................... Introduccion a InterBase 736 ............................................................................................................ Uso de IBConsole 738 Programacion de servidor en InterBase ........................................................................ 740 Procedimientos almacenados ................................................................................. 740 Disparadores (y generadores) ................................................................................... 741 La biblioteca dbExpress ....................................................................................................... 743 Trabajo con cursores unidireccionales ..................................................................... 743 Plataformas y bases de datos ........................................................................................ 744 .................... Problemas con las versiones de controladores e inclusion de unidades 745 Los componentes dbExpress ................................................................................................ 746 El componente SQLConnection .................................................................................... 747 ............................................... Los componentes de conjuntos de datos de dbExpress 751 ........................................................... El componente SimpleDataSet de Delphi 7 752 El componente SQLMonitor .......................................................................................... 753 Algunos ejemplos de dbExpress .................................................................................... 754 Uso de un componente unico o de varios ..................................................................... 755 Aplicacion de actualizaciones .................................................................................. 755 . . Seguimiento de la conexion ..................................................................................... 756 Control del codigo SQL de actualizacion ............................................................... 757 Acceso a metadatos de la base de datos con SetSchemaInfo ................................ 758 ............................................................................................... Una consulta parametrica 760 ....................................................... Cuando basta una sola direccion: imprimir datas 762 Los paquetes y la cache ........................................................................................................ 765 Manipulacion de actualizaciones .................................................................................. 766 El estado de 10s registros .......................................................................................766 Acceso a Delta ........................................................................................................... 767 Actualizar 10s datos .................................................................................................... 768 Uso de transacciones ....................................................................................................... 771 Uso de InterBase Express ............................................................................................... 774 Componentes de conjunto de datos IBX ..................................................................... 776 Componentes administrativos IBX .......................................................................... 777 Creacion de un ejemplo IBX ....................................................................................... 777 Creacion de una consulta en vivo .................................................................................. 779 Control en InterBase Express ........................................................................................ 783 Obtencion de mas datos de sistema ............................................................................... 784 Bloques del mundo real ....................................................................................................... 785 Generadores e identificadores ........................................................................................ 786 Busquedas sin distincion entre mayusculas y minusculas .......................................... 788 Manejo de ubicaciones y personas ........................................................................... 790 Creacion de una interfaz de usuario .......................................................................... 792 Reserva de clases ............................................................................................................. 795 Creacion de un dialogo de busqueda ............................................................................. 798 Adicion de un formulario de consulta libre ................................................................ 800 15 . Trabajo con ADO .......................................................................................................... 803 Microsoft Data Access Componentes (MDAC) ............................................................. 805 Proveedores de OLE DB ............................................................................................. 805 Uso de componentes dbGo ................................................................................................... 807 Un ejemplo practico ................................................................................................... 808 El componente ADOConnection ............................................................................. 811 Archivos de enlace de datos ......................................................................................... 811 Propiedades dinamicas ....................................................................................................... 812 Obtencion de information esquematica ............................................................................ 813 Uso del motor Jet ............................................................................................................. 815 Paradox a traves de Jet ................................................................................................... 816 Excel a traves de Jet ....................................................................................................... 817 Archivos de texto a traves de Jet ............................................................................. 819 . , Importaclon y exportation ............................................................................................ 821 Trabajo con cursores ............................................................................................................. 822 . . Ubicacion de cursor ................................................................................................... 822 Tipo de cursor .................................................................................................................. 823 Pedir y no recibir ............................................................................................................. 825 ................................................................................................ Sin recuento de registros 826 Indices de cliente ............................................................................................................. 826 . . . Repllcaclon ...................................................................................................................... 827 Procesamiento de transacciones .................................................................................... 829 Transacciones anidadas ........................................................................................... 830 Atributos de ADOConnection ................................................................................... 830 Tipos de bloqueo ............................................................................................................. 831 . . El bloqueo peslmlsta ............................................................................................. 832 Actualizacion de 10s datos ................................................................................................... 832 ............................................................................................... Actualizaciones por lotes 834 Bloqueo optimists ........................................................................................................... 836 Resolution de conflictos de actualizacion .................................................................... 839 Conjuntos de registros desconectados ................................................................................ 840 .......................................................................................................... Pooling de conexiones 841 Conjuntos de registros permanentes ............................................................................. 843 ...................................................................................................... El modelo de maletin 844 Unas palabras sobre ALIO.NET ........................................................................................... 845 16 . Aplicaciones DataSnap multicapa ............................................................................. 847 ................................................................ Niveles uno. dos y tres en la historia de Delphi 848 Fundamento tecnico de DataSnap ................................................................................. 850 La interfaz AppSener .................................................................................................... 850 Protocolo de conexion ..................................................................................................... 851 Proporcionar paquetes de datos ..................................................................................... 853 Componentes de soporte Delphi (entorno cliente) .................................................... 854 .................................................... Componentes de soporte Delphi (entorno senidor) 856 Construction de una aplicacion de ejemplo ...................................................................... 856 El primer senidor de aplicacion ................................................................................... 856 El primer cliente ligero .................................................................................................. 858 Adicion de restricciones a1 senidor ....................................................................................860 Restricciones de campo y conjuntos de datos .............................................................. 860 Inclusion de propiedades de campo .............................................................................. 862 Eventos de campo y tabla ............................................................................................... 862 Adicion de caracteristicas a1 cliente ................................................................................... 863 Secuencia de actualization ............................................................................................ 864 Refresco de datos ............................................................................................................. 865 Caracteristicas avanzadas de DataSnap ............................................................................. 867 Consultas por parametros ............................................................................................... 868 Llamadas a metodos personalizados ............................................................................. 868 Relaciones maestroldetalle ............................................................................................. 870 ............................................................................................ Uso del agente de conexion 871 Mas opciones de proveedor ............................................................................................ 872 Agente simple de objetos ................................................................................................ 873 Pooling de objetos ........................................................................................................... 874 Personalizacion de paquetes de datos ........................................................................... 874 17 . CreacMn de componentes de bases de datos ........................................................... 877 El enlace de datos ................................................................................................................. 878 La clase TDataLink ......................................................................................................... 878 Clases de enlaces de datos derivadas ............................................................................ 879 Creacion de controles data-aware orientados a campos .................................................. 880 Una ProgressBar de solo lectura ................................................................................... 880 Una TrackBar de lectura y escritura ............................................................................. 884 Creacion de enlaces de datos personalizados .................................................................... 887 Un componente visualizador de registros .................................................................... 888 .......................................................................... Personalizacion del componente DBGrid 893 .................................................................... Construir conjuntos de datos personalizados 897 La definicion de las clases ............................................................................................. 898 Apartado I: Inicio. apertura y cierre ............................................................................. 902 ..................................................... Apartado 11: Movimiento y gestion de marcadores 907 ............................................... Apartado 111: Buffers de registro y gestion de campos 911 Apartado IV: De buffers a campos ................................................................................ 915 .............................................. Comprobacion el conjunto de datos basado en streams 917 Un directorio en un conjunto de datos ............................................................................... 918 .................................................................................. Una lista como conjunto de datos 919 ......................................................................................................... Datos del directorio 920 Un conjunto de datos de objetos .......................................................................................... 924 . . 18 . Generation de informes con Rave ............................................................................. 931 Presentation de Rave ............................................................................................................ 932 Rave: el entorno visual de creacion de informes ......................................................... 933 El Page Designer y el Event Editor ..................................................................... 934 El panel Property .................................................................................................. 934 El panel Project Tree ................................................................................................. 934 Barras de herramientas y la Toolbar Palette .......................................................... 935 La barra de estado ..................................................................................................... 936 Uso del componente RvProject ...................................................................................... 936 ........................................................................................... Formatos de representacion 938 Conexiones de datos ....................................................................................................... 939 ........................................................................................ Componentes del Rave Designer 941 Componentes basicos ...................................................................................................... 942 Componentes Text y Memo ...................................................................................... 942 El componente Section ............................................................................................. 942 Componentes grhficos ............................................................................................... 943 El componente FontMaster ...................................................................................... 943 .................................................................................................... Numeros de pagina 944 Componentes de dibujo ............................................................................................. 944 Componentes de codigo de barras ........................................................................... 944 Objetos de acceso a datos ............................................................................................... 945 Regiones y bandas ........................................................................................................... 946 El Band Style Editor ................................................................................................. 947 Componentes data-aware ............................................................................................... 949 El Data Text Editor ................................................................................................... 949 De Text a Memo ........................................................................................................ 950 Calculo de totales ...................................................................................................... 951 ................................................................................ Repeticion de datos en paginas 951 ....................................................................................................................... Rave avanzado 951 ................................................................................................Informes maestro-detalle 952 Guiones de informes ....................................................................................................... 953 Espejos .............................................................................................................................. 954 Calculos a tope ................................................................................................................ 955 .................................................................................................................... CalcTotal 955 Parte IV . Delphi e Internet .............................................................................................. 959 19 . Programacidn para Internet: sockets e Indy ......................................................... 961 Creacion de aplicaciones con sockets ................................................................................. 962 Bases de la programacion de sockets ............................................................................ 963 Configuracion de una red local: direcciones IP ................................................ 964 Nombres de dominio local ........................................................................................ 964 ............................................................................................................... Puertos TCP 964 Protocolos de alto nivel ............................................................................................ 965 Conexiones de socket ................................................................................................ 965 Uso de componentes TCP de Indy ................................................................................. 966 Envio de datos de una base de datos a traves de una conexion de socket ................ 970 Envio y recepcion de correo electronic0 ....................................................................... 973 Correo recibido y enviado .............................................................................................. 975 ................................................................................................................ Trabajo con HTTP 977 Obtencion de contenido HTTP ................................................................................. 978 La M I WinInet .......................................................................................................... 982 Un navegador propio ...................................................................................................... 983 Un sencillo servidor HTTP ............................................................................................ 985 Generacion de HTML ........................................................................................................... 987 Los componentes productores de codigo HTML de Delphi ........................................ 987 Generacion de paginas HTML ....................................................................................... 988 Creacion de paginas de datos ................................................................................. 990 Produccion de tablas HTML .......................................................................................... 991 Uso de hojas de estilo ..................................................................................................... 993 Paginas dinamicas de un servidor personalizado ........................................................ 994 20 . Programacidn Web con WebBroker y WebSnap .................................................... 997 Paginas Web dinarnicas .................................................................................................. 998 ........................................................................................................ Un resumen de CGI 999 Uso de bibliotecas dinamicas ....................................................................................... 1000 Tecnologia WebBroker de Delphi ..................................................................................... 1001 Depuracion con Web App Debugger ...................................................................... 1004 ............................................................... Creacion de un WebModule multiproposito 1007 Informes dinamicos de base de datos .......................................................................... 1009 ................................................................................................ Consultas y formularios 1010 Trabajo con Apache ...................................................................................................... 1014 Ejemplos practices .............................................................................................................. 1016 ............................................................................ Un contador Web grafico de visitas 1017 .............................................................. Busquedas con un motor Web de busquedas 1019 WebSnap ............................................................................................................................ 1021 . , , . Gestion de varias paglnas ........................................................................................ 1025 Guiones de servidor ...................................................................................................... 1027 Adaptadores ................................................................................................................... 1030 Campos de adaptadores .......................................................................................... 1030 Componentes de adaptadores ................................................................................. 1031 Uso del Adapterpageproducer ........................................................................... 1031 Guiones en lugar de codigo .................................................................................... 1034 ........................................................................................................ Encontrar archivos 1035 WebSnap y bases de datos .................................................................................................. 1036 Un modulo de datos WebSnap ..................................................................................... 1036 ........................................................................................................ El DataSetAdapter 1036 Edicion de 10s datos en un formulario ........................................................................ 1039 Maestro/Detalle en WebSnap ................................................................................... 1041 Sesiones, usuarios y permisos ........................................................................................... 1043 Uso de sesiones .............................................................................................................. 1043 Peticion de entrada en el sistema ............................................................................ 1045 Derechos de acceso a una unica pagina .............................................................. 1047 ........................................................................... 21 . Programacibn Web con IntraWeb 1049 Introduccion a IntraWeb ............................................................................................... 1050 De sitios Web a aplicaciones Web ........................................................................... 1051 Un primer vistazo interior ...................................................................................... 1054 Arquitecturas IntraWeb .......................................................................................... 1057 Creacion del aplicaciones IntraWeb ............................................................................1058 Escritura de aplicaciones de varias paginas .......................................................... 1060 Gestion de sesiones ................................................................................................. 1064 Integracion con WebBroker (y WebSnap) .............................................................. 1066 Control de la estructura ................................................................................................ 1068 Aplicaciones Web de bases de datos ................................................................................. 1070 Enlaces con detalles ...................................................................................................... 1072 ...................................................................................... Transporte de datos a1 cliente 1076 22 . Uso de tecnologias XML ............................................................................................ 1079 Presentacion de XML ......................................................................................................... 1080 Sintaxis XML basica .................................................................................................. 1080 XML bien formado ........................................................................................................ 1082 .......................................................................................................... Trabajo con XML 1083 Manejo de documentos XML en Delphi .............................................................. 1084 Programacion con DOM .................................................................................................... 1085 Un documento XML en una TreeView ................................................................... 1087 ................................................................. Creacion de documentos utilizando DOM 1090 Interfaces de enlace de datos XML ......................................................................... 1094 Validacion y esquemas ............................................................................................ 1098 Uso de la API de SAX .................................................................................................. 1099 Proyeccion de XML con transformaciones ................................................................. 1103 XML e Internet Express ..................................................................................................... 1108 ......................................................................................... El componente XMLBroker 1109 Soporte de JavaScript ................................................................................................... 1110 Creacion de un ejemplo ........................................................................................... 1111 Uso de XSLT ....................................................................................................................... 1116 Uso de XPath ................................................................................................................. 11 17 XSLT en la practica ...................................................................................................... 1118 XSLT con WebSnap ...................................................................................................... 1119 Transformaciones XSL directas con DOM ................................................................. 1121 Procesamiento de grandes documentos XML ........................................................... 1123 De un ClientDataSet a un documento XML ............................................................ 1123 De un documento XML a un ClientDataSet ............................................................ 1125 23 . Semicios Web y SOAP ............................................................................................... 1129 Servicios Web ................................................................................................................... 1130 SOAP y WSDL .............................................................................................................. 1130 Traducciones BabelFish ........................................................................................ 1131 Creacion de un servicio Web ....................................................................................... 1134 Un servicio Web de conversion de divisas ............................................................... 1135 Publicacion del WSDL ............................................................................................ 1136 Creacion de un cliente personalizado ............................................................... 1137 Peticion de datos de una base de datos ................................................................... 1139 Acceso a 10s datos ................................................................................................... 1139 Paso de documentos XML ...................................................................................... 1140 El programa cliente (con proyeccion XML) ......................................................... 1142 Depuracion de las cabeceras SOAP ............................................................................ 1143 Exponer una clase ya existente como un servicio Web ............................................. 1144 DataSnap sobre SOAP ........................................................................................................ 1145 Creacion del semidor SOAP DataSnap ...................................................................... 1145 Creacion del cliente SOAP DataSnap ......................................................................... 1148 SOAP frente a otras conexion con DataSnap ............................................................. 1148 Manejo de adjuntos ............................................................................................................. 1149 Soporte de UDDI ................................................................................................................. 1151 ~QuC es UDDI? .............................................................................................................. 1151 UDDI en Delphi 7 ......................................................................................................... 1153 Parte V . ApCndices ............................................................................................................ 1157 ApCndice A. Herramientas Delphi del autor ............................................................... 1159 CanTools Wizards ............................................................................................................... 1159 Programa de conversion VclToClx ................................................................................... 1162 Object Debugger ................................................................................................................. 1162 Memory Snap ...................................................................................................................... 1163 Licencias y contribuciones ................................................................................................. 1164 ApCndice B . Contenido del CD-ROM ........................................................................... 1165 lntroduccion La primera vez que Zack Urlocker me enseiio un product0 aun sin publicar denominado Delphi, me di cuenta de que cambiaria mi trabajo (y el trabajo de muchos otros desarrolladores de software). Solia pelearme con bibliotecas de C++ para Windows y, Delphi era, y todavia es, la mejor combinacion de progra- macion orientada a objetos y programacion visual no solo para este sistema ope- rativo sino tambien para Linux y pronto para .NET. Delphi 7 simplementese suma a esta tradicion, sobre las solidas bases de la VCL, para proporcionar otra impresionante herramienta de desarrollo de soft- ware que lo coordina todo. iEsta buscando soluciones de bases de datos, clientel servidor, multicapa (multitier), Intranet o Internet? iBusca control y potencia? ~ B U S C ~ una rapida productividad? Con Delphi y la multitud de tecnicas y trucos que se presentan en este libro, sera capaz de conseguir todo eso. Siete versiones y contando Algunas de las propiedades originales de Delphi que me atrajeron heron su enfoque orientado a objetos y basado en formularios, su compilador extremada- mente rapido, su gran soporte para bases de datos, su estrecha integracion con la programacion para Windows y su tecnologia de componentes. Pero el elemento mas importante era el lenguaje Pascal orientado a objetos, que es la base de todo lo demas. iDelphi 2 era incluso mejor! Entre sus propiedades aiiadidas mas importantes estaban las siguientes: El Multi Record Object y la cuadricula para bases de datos mejorada, el soporte para Automatizacion OLE y el tipo de datos variantes, el soporte e integracion totales de Windows 95, el tip0 de datos de cadena larga y la herencia de formulario visual. Delphi 3 aiiadio la tecnologia Code Insight, el soporte de depuracion DLL, las plantillas de componentes, el Teechart, el Decision Cube, la tecnologia WebBroker, 10s paquetes de componentes, 10s ActiveForms y una sorprendente integracion con COM, gracias a las interfaces. Delphi 4 nos trajo el editor AppBrowser, nuevas propiedades de Windows 98, mejor soporte OLE y COM, componentes de bases de datos ampliados y muchas mas clases principales de la VCL aiiadidas, como el soporte para acoplamiento, restriccion y anclaje de 10s controles. Delphi 5 aiiadio a este cuadro muchas mejoras en el IDE (demasiadas para enumerarlas aqui), soporte ampliado para bases de datos (con conjuntos de datos especificos de ADO e InterBase), una version mejorada de MIDAS con soporte para Internet, la herramienta de control de versiones Teamsource, capacidades de traduccion, el concept0 de marcos y nuevos componentes. Delphi 6 aiiadio a todas estas propiedades el soporte para el desarrollo multiplataforma con la nueva biblioteca de componentes para multiplataforma (CLX), una biblioteca en tiempo de ejecucion ampliada, el motor para base de datos dbExpress, un soporte excepcional de servicios Web y XML, un poderoso marco de trabajo de desarrollo Web, mas mejoras en el IDE y multitud de compo- nentes y clases, que siguen comentandose en las paginas siguientes. Delphi 7 proporciono mas robustez a estas nuevas tecnologias con mejoras y arreglos (el soporte de SOAP y DataSnap es lo primer0 en lo que puedo pensar) y ofrece soporte para tecnologias m h novedosas (como 10s temas de Windows XP o UDDI), per0 lo mas importante es que permite disponer rapidamente de un interesante conjunto de herramientas de terceras partes: el motor de generacion de informes RAVE, la tecnologia de desarrollo de aplicaciones Web IntraWeb y el entorno de diseiio ModelMaker. Finalmente, abre las puertas aun mundo nuevo a1 ofrecer (aunque sea como prueba) el primer compilador de Borland para el len- guaje PascallDelphi no orientado a la CPU de Intel, si no a la plataforma CIL de .NET. Delphi es una gran herramienta, per0 es tambien un entorno de programacion completo en el que hay muchos elementos involucrados. Este libro le ayudara a dominar la programacion en Delphi, incluidos el lenguaje Delphi, 10s componen- tes (a usar 10s existentes y crear otros propios), el soporte de bases de datos y clientelservidor, 10s elementos clave de programacion en Windows y COM y el desarrollo para Web e Internet. No necesita tener un amplio conocimiento de estos temas para leer el libro, per0 es necesario que conozca las bases de la programacion. Le ayudara conside- rablemente el estar familiarizado con el lenguaje Delphi, sobre todo despues de 10s capitulos introductorios. El libro comienza a tratar 10s temas con detenimiento de forma inmediata; se ha eliminado gran parte del material introductorio incluido en otros textos. La estructura del libro El libro se divide en cinco partes: Parte I: Bases. Introduce las nuevas propiedades del entorno de desarrollo integrado (IDE) de Delphi 7 en el capitulo 1, a continuacion pasa a1 len- guaje Delphi y a la biblioteca en tiempo de ejecucion (RTL) y la biblioteca de componentes visuales (VCL). Cuatro capitulos proporcionan las bases y explicaciones avanzadas sobre 10s controles mas usados, el desarrollo de interfaces de usuario avanzadas y el uso de formularies. Parte 11: Arquitecturas orientadas a objetos en Delphi. Trata las aplicacio- nes Delphi, el desarrollo de componentes personalizados, el uso de biblio- tecas y paquetes, el uso de ModelMaker y COM+. Parte 111: Arquitecturas orientadas a bases de datos en Delphi. Trata sobre el acceso simple a las bases de datos, la explicacion pormenorizada de 10s controles data-aware, la programacion clientelservidor, dbExpress, InterBase, ADO, Datasnap, el desarrollo de controles data-aware y con- juntos de datos personalizados y la generacion de informes. Parte IV: Delphi e Internet. Trata en primer lugar sobre 10s sockets TCPI IP, 10s protocolos de Internet e Indy, y despues pasa a areas especificas como las extensiones del lado del servidor Web (con WebBroker, WebSnap e IntraWeb) y acaba con XML y el desarrollo de servicios Web. Parte V: Apendices. Describe las herramientas extra de Delphi y el conte- nido del CD-ROM que acompaiia a1 libro. Tal como sugiere este breve resumen, el libro trata muchos temas de interes para 10s usuarios de Delphi con casi cualquier nivel de experiencia en programa- cion, desde "principiantes avanzados" a desarrolladores de componentes. En el libro, he intentado eludir el material de referencia casi por completo y me he centrado, en cambio, en las tecnicas para utilizar Delphi de forma efectiva. Dado que Delphi ofrece amplia documentacion electronica, incluir listas sobre metodos y propiedades de componentes en el libro resultaria superfluo y haria que la obra quedase obsoleta en cuanto el software sufriese pequeiios cambios. Para tener material de referencia disponible, le sugiero que lea el libro con 10s archivos de Ayuda de Delphi a mano. Sin embargo, he hecho todo lo posible para que el libro se pueda leer lejos del ordenador, si asi se prefiere. Las capturas de pantalla y 10s fragmentos clave de 10s listados deberian ayudarle en ese sentido. El libro utiliza unicamente unas cuantas convenciones para resultar mas legible. Normas usadas en este libro En este libro se usan las siguientes convenciones tipograficas: Las opciones de menus se indican en orden jerarquico, con cada instruc- cion de menu separada por el signo "mayor que" y en un tip0 de letra Arial. Por ejemplo, File>Open quiere decir hacer clic en el comando File en la barra de menu y luego seleccionar Open. Todos 10s elementos del codigo fuente, como las palabras clave, las pro- piedades, las clases y las funciones, aparecen en un tipo de l e t r a c o u r i e r y 10s fragmentos de codigo poseen el mismo formato que el utilizado en el editor Delphi, a saber, las palabras claves en negrita y 10s comentarios y cadenas en cursiva. Las combinaciones de teclas se indican de esta forma: Control-C. A lo largo del libro encontrara unos rectangulos sombreados que resaltan la informacion especial o importante, por ejemplo: ADVERTENCIA: Indica un procedimiento que, en teoriq podria causar dificultades o incluso la ptr&da de datos. - NOTA: Resalta la informacion interesante o erdicional y suele contener pequefios trozos extra de informaci6n tecnica sobrc dn terna. -- - -- - -- TRUCO: Llamm la atenci6n sobre habiles sugerencias, pistas recomenda- bles y consejos 6tiles. Bases Delphi 7 En una herramienta de programacion visual como Delphi,el papel del Entorno de Desarrollo Integrado (IDE, Integrated Development Environment) resulta a veces mas importante que el lenguaje de programacion. Delphi 7 ofrece algunas nuevas caracteristicas muy interesantes sobre el maravilloso IDE de Delphi 6. En este capitulo examinaremos estas nuevas caracteristicas, a1 igual que las caracte- risticas aiiadidas en otras versiones recientes de Delphi. Tambien comentaremos unas cuantas caracteristicas tradicionales de Delphi que no son bien conocidas u obvias a 10s recien llegados. Este capitulo no es un tutorial completo sobre el IDE, que necesitaria mucho mas espacio; principalmente es un conjunto de conse- jos y sugerencias dirigidas a1 usuario medio de Delphi. Si se trata de un progra- mador novato, no se preocupe. El IDE de Delphi es bastante intuitivo. El propio Delphi incluye un manual (disponible en formato Acrobat en el CD Delphi Companion Tools) con un tutorial que presenta el desarrollo de aplicaciones en Delphi. Puede encontrar una introduccion mas sencilla a Delphi y su IDE en otros textos. Pero en este libro asumiremos que ya sabe como llevar a cab0 las opera- ciones basicas del IDE; todos 10s capitulos despues de este se centraran en cues- tiones y tecnicas de programacion. Este capitulo trata 10s siguientes temas: Navegacion del IDE. El editor. La tecnologia Code Insight. Diseiio de formularios. El Project Manager Archivos de Delphi. Ediciones de Delphi Antes de pasar a 10s pormenores del entorno de programacion de Delphi, resal- taremos dos ideas clave. En primer lugar, no hay una unica edicion de Delphi, sino muchas. En segundo lugar, cualquier entorno Delphi se puede personalizar. Por dichas razones, las pantallas de Delphi que aparecen en este capitulo pueden ser distintas a las que vea en su ordenador. Las ediciones de Delphi actuales son las siguientes: La edicion "Personal": Dirigida a quienes empiezan a utilizar Delphi y a programadores esporadicos. No soporta programacion de bases de datos ni ninguna de las caracteristicas avanzadas de Delphi. La edicion "Professional Studio": Dirigida a desarrolladores profesiona- les. Posee todas las caracteristicas basicas, mas soporte para programa- cion de bases de datos (corno soporte ADO), soporte basico para servidores Web (WebBroker) y algunas herramientas externas como ModelMaker e IntraWeb. En el libro se asume que el lector trabaja como minimo con la edicion Professional. La edici6n "Enterprise Studio": Esta dirigida a desarrolladores que crean aplicaciones para empresas. Incluye todas las tecnologias XML y de servi- cios Web avanzados, soporte de CORBA, internacionalizacion, arquitec- tura en tres niveles y muchas otras herramientas. Algunos capitulos del libro tratan sobre caracteristicas que solo posee esta version de Delphi y asi se ha especificado en esos casos. La edicihn "Architect Studio": Aiiade a la edicion Enterprise el soporte de Bold, un entorno para la creacion de aplicaciones dirigidas en tiempo de ejecucion por un modelo UML y capaces de proyectar sus objetos tanto sobre una base de datos como sobre una interfaz de usuarios, gracias a una gran cantidad de componentes avanzados. El soporte de Bold no se trata en este libro. Ademas de las distintas versiones disponibles, existen varias formas de perso- nalizar el entorno Delphi. En las capturas de pantalla presentadas a lo largo del libro, se ha intentado utilizar una interfaz estandar (corno la que resulta de la instalacion tal cual). Sin embargo, en ciertos ejemplos, pueden aparecer refleja- das algunas preferencias del autor como la instalacion de muchos aiiadidos, que pueden reflejarse en el aspect0 de las pantallas. La version Professional y supe- riores de Delphi 7 incluyen una copia funcional de Kylix 3, en la edicion de lenguaje Delphi. Ademas de referencias a la biblioteca CLX y a las caracteristi- cas multiplataforma de Delphi, este libro no trata Kylix ni el desarrollo sobre Linux. Puede buscar otras obras para conseguir mas informacion sobre este tema. (No hay muchas diferencias entre Kylix 2 y Kylix 3 en la version de lenguaje Delphi. La caracteristica nueva mas importante de Kylix 3 es su soporte del lenguaje C++.) Una vision global del IDE Cuando se trabaja con un entorno de desarrollo visual, el tiempo se emplea en dos partes distintas de la aplicacion: en 10s asistentes de disefio visual y en el editor de codigo. Los asistentes de diseiio permiten trabajar con componentes a un nivel visual (como cuando se coloca un boton sobre un formulario) o a un nivel no visual (como cuando se situa un componente DataSet sobre un modulo de datos). La figura 1.1 muestra un formulario y un modulo de datos en accion. En ambos casos, 10s asistentes de diseiio permiten escoger 10s componentes necesarios y fijar el valor inicial de las propiedades de 10s componentes. Figura 1.1. Un formulario y un modulo de datos en el IDE de Delphi 7. rn El editor de codigo es donde se escribe el codigo. El mod0 mas obvio de escribir codigo en un entorno visual implica responder a eventos, comenzando por 10s eventos enlazados con las operaciones realizadas por 10s usuarios del progra- ma, como hacer clic sobre un boton o escoger un elemento de un cuadro de lista. Puede usarse el mismo enfoque para manejar eventos internos, como 10s eventos que implican cambios en bases de datos o notificaciones del sistema operativo. A medida que 10s programadores adquieren un mayor conocimiento sobre Delphi, suelen comenzar escribiendo basicamente codigo gestor de eventos y des- pues escriben sus propias clases y componentes y, normalmente, acaban invir- tiendo la mayor parte de su tiempo en el editor. Ya que este libro trata mas conceptos que la programacion visual e intenta ayudar a dominar toda la potencia de Delphi, a medida que el testo avance se vera mas codigo y menos formularios. Un IDE para dos bibliotecas Por primera vez en Delphi 6 aparecio un importante cambio. El IDE permite ahora utilizar dos bibliotecas de componentes distintas: la VCL (Visual Cornpo- nente Library, Biblioteca de componentes visuales) y la CLX (Component Library for Cross-Platform, Biblioteca de componentes para multiplataforma). Cuando creamos un nuevo proyecto, sencillamente escogemos cual de las dos bibliotecas queremos emplear, con las opciones de menu File>New>Application en el caso de un clasico programa Windows basado en la VCL y con las opciones File>New>CLX Application en el caso de una nueva aplicacion que se puede transportar basada en la CLX. blpbi w-pefliiik,~mmpi&p 91 cbdi& +g G& para, qv fwnicqk bajc Fioux. Re- ipte~eaante:vt&w CLS De& 7, ya que ip versi6n para lenguaje lM@i dc K y b se dkstribuye junto con el pv&tr;topam ~ind'ms, Al crear un nuevo proyecto o abrir uno que ya existe, la Component Palette se reorganiza para mostrar solo 10s controles relacionados con la biblioteca en uso (aunque en realidad la mayoria de 10s controles son compartidos). Cuando se traba con un diseiiador no visual (como un modulo de datos), las pestaiias de la Component Palette que muestran solo 10s componentes visuales se ocultan de la vista. Configuracion del escritorio Los programadores pueden personalizar el IDE de Delphi de varias maneras (tipicamente abriendo muchas ventanas, reordenandolas, y acoplandolas entre si). Sin embargo, normalmente sera necesario abrir un conjunto de ventanas en tiem- po de diseiio y un conjunto distinto en tiempo de depuracion. Del mismo modo, podria necesitarse una disposicion cuando se trabaje con formularios y otra com- pletamente diferente cuando se escriban componentes o codigo de bajo nivel me- diante el unico uso del editor. Reorganizar el IDE para cada una de estas necesidades es una tarea tediosa. Por este motivo, Delphi permite almacenar una determinada disposicion de las ventanas del IDE (llamada escritorio o escritorio global (Global Desktop) para distinguirlo de un escritorio deproyecto (Project Desktop) con un nombre y recu- perarla rapidamente. Tambien se puede convertir a una de estas agrupaciones en la configuracion predeterminada para la depuracion, de manera que se recuperara automaticamente cuando se inicie el depurador. Todas estas caracteristicas estan disponibles en la barra de herramientas Desktops. Tambien puede trabajar con las configuraciones de escritorio mediante el menu View>Desktops. La informacion de configuracion de escritorio se guarda en archivos DST (dentro del directorio b i n de Delphi), que en realidad son archivos INI. Los parametros guardados incluyen la posicion de la ventana principal, el Project Manager, la Alignment Palette, el Object Inspector (incluida su configura- cion de categorias de propiedades), el editor de ventanas (con el estado del Code Explorer y la Message View) y muchos otros, ademb del estado de anclaje de las diversas ventanas. Este es un pequeiio extract0 de un archivo DST, que debe- ria resultar facil de leer: [Main Window] Create=l Visible=l State=O Left=O Top=O Width=1024 Height=105 ClientWidth=1016 ClientHeight=78 [Alignmentpalette] Create=l Visible=O Las configuraciones de escritorio tienen mas fierza que las configuraciones de proyecto, que se guardan en un archivo DSK con una estructura similar. Las configuraciones de escritorio ayudan a eliminar problemas que pueden suceder cuando se traslada un proyecto de una maquina a otra (o de un desarrollador a otro) y es necesario reorganizar las ventanas a1 gusto. Delphi separa las configu- raciones de escritorio globales por usuario y las configuraciones de escritorio por proyecto, para ofrecer un mejor soporte a equipos de desarrollo. - -- -- 7 ---- - TRUCO: Si se abre Delphi y no se puede ver el formulario u otras vent.- nas, es recornendable cornprobar (o borrar) las configuraciones dti 'escrito- rio (en el directorio bin de Delphi). Si se abre un proyecto recibido de un usuario clistinto y no se pueden ver algunas de las ventanas o no gusta la disposici6n del escritorio, lo mejor es volver a cargar las c o n f i s e i o n e s de 10s escritorios globales o borrar el archivo DSK del proyccto. . Environment Options Unas cuantas de las ultimas mejoras tienen que ver con el habitual cuadro de dialogo Environment Options. Las paginas de este cuadro de dialogo se reorga- nizaron en Delphi 6, desplazando las opciones del Form Designer de la pagina Preferences a la nueva pagina Designer. En Delphi 6 tambien existian unas cuantas opciones y paginas nuevas: La pagina Preferences del cuadro de dialogo: Time una casilla de veri- ficacion que impide que las ventanas de Delphi se acoplen automaticamente entre si. La pagina Environment Variables: Permite inspeccionar las variables del entorno del sistema (como las rutas predefinidas y parametros del SO) y establecer variables definidas por el usuario. Lo bueno es que se pueden utilizar ambos tipos de variable en cada uno de 10s cuadros de dialogo del IDE (por ejemplo, se puede evitar escribir explicitamente rutas usadas habitualmente, sustituyendolas por una variable). En otras palabras, las variables del entorno funcionan de manera similar a la variable $DELPHI, que hace referencia a1 directorio base de Delphi per0 puede ser definida por el usuario. L a pagina Internet: En ella se pueden escoger cuales son las extensiones de archivo predefinidas para 10s archivos HTML y SML (basicamente por el marco de trabajo WebSnap) y tambien asociar un editor externo con cada extension. Sobre 10s menus La principal barra de menu de Delphi (que en Delphi 7 tiene un aspect0 mas moderno) es un metodo importante de interaccion con el IDE, aunque probable- mcnte la mayoria de las tareas se realizaran mediante atajos de teclado y de menu. La barra de menu no cambia demasiado como reaccion a la operacion actual: se necesita hacer clic con el boton derecho del raton para conseguir una lista de las operaciones que se pueden realizar en la ventana o componente actual. La barra de menu cambia en gran medida segun las herramientas y asistentes de terceras partes que se hayan instalado. En Delphi 7, ModelMaker dispone de su propio menu. Si se instalan modulos adicionales como GExperts se pueden contemplar otros menus. Un importante menu afiadido a Delphi en las versiones mas recientes es el menu Window del IDE. Este menu muestra la lista de las ventanas abiertas; antes, se podia obtener esta lista mediante la combinacion de teclas Alt-0 o la opcion de menu View>Window List. El menu Window resulta realmente practico, ya que las ventanas suelen acabar detras de otras y son dificiles de encontrar. Puede controlarse el orden alfabetico de este menu mediante un parametro del Registro de Windows: hay que encontrar la subclave Main Window de Delphi (dentro de HKEY CURRENT USER\Software\Borland\Delphi\7.0). Esta c law del ~ e g i s t r o utiliz; una cadena (en lugar de valores booleanos), donde -1 y True indican verdadero y 0 y False indican falso. TRUCO: En Delphi 7, el menu Windows finaliza con un comando nuevo: Next Window. Este comando resulta particularmente util como atajo, ALGF6p. Se pueden recorrer las diversas ventanas del IDE de manera muy sencilla olediante este comando. El cuadro de dialogo Environment Options Como ya se ha comentado, algunos de 10s parametros del IDE necesitan que se edite directamente el Registro. Por supuesto, 10s parametros mas comunes pueden ajustarse simplemente mediante el cuadro de dialogo Environment Options, que esta disponible a traves del menu TOOIS junto con Editor Options y Debugger Options. La mayor parte de 10s parametros resultan bastante intuitivos y estan bien descritos en el archivo de ayuda de Delphi. La figura 1.2 muestra mis parametros estandar para la pagina Preferences de este cuadro de dialogo. TO-DO List Otra caracteristica aiiadida en Delphi 5 pero que aun sigue sin usarse como deberia es la lista de tareas pendientes. Se trata de una lista de tareas que aun se debe realizar para completar un proyecto (es un conjunto de notas para el progra- mador o programadores, que resulta una herramienta muy util en un equipo). Aunque la idea no es novedosa, el concept0 clave de la lista de tareas pendientes en Delphi es que funciona como una herramienta de dos vias. Figura 1.2. La pagina Preferences del cuadro de dialogo Environment Options. Se pueden aiiadir o modificar elementos pendientes a esta lista afiadiendo co- mentarios TODO al codigo fuente de cualquier archivo de un proyecto; se pueden ver las entradas correspondientes en la lista. Ademas, se pueden editar visualmente 10s elementos de la lista para modificar el comentario correspondiente en el codi- go fuente. Por ejemplo, este es el aspect0 que mostraria un elemento de la lista de tareas pendientes en el codigo fuente: procedure TForml.FormCreate(Sender: TObject); begin / / TODO - o M a r c o : A i i a d i r cddigo d e creacidn end; El mismo elemento puede editarse visualmente en la ventana que muestra la figura 1.3, dentro de la ventana To-Do List. La excepcion a esta regla de las dos vias es la definition de elementos pendien- tes en el ambito del proyecto. Debe aiiadir directamente estos elementos a la lista. Para hacer esto, puede utilizar la combinacion de teclas Control-A dentro de la ventana To-Do List o hacer clic con el boton derecho sobre la ventana y seleccio- nar la opcion Add en el menu desplegable. Estos elementos se guardan en un archivo especial con el mismo nombre raiz que el archivo del proyecto y una extension .TODO. Pueden utilizarse diversas opciones con un comentario TODO. Puede usarse -0 (como en el ejemplo anterior) para indicar el propietario (el programador que escribio el comentario), la opcion -c para indicar una categoria, o simplemente un numero de 1 a 5 para indicar la prioridad (0 , o ningun numero, indica que no se establece ningun nivel de prioridad).Por ejemplo, a1 usar el comando Add T o - D o I t e m del menu desplegable del editor (o la combinacion Control-Mayus- T ) se genero este comentario: TODO 2 -oMarco : B u t t o n p r e s s e d } Delphi trata todo lo que aparezca tras 10s dos puntos (hasta el final de la linea o hasta la Have de cierre, segun el tipo de comentario), como el texto del elemento de tarea pendiente. 1 * _. ---. . - --' - A A c m llcm I ! I ~ o a ~ e 10- IWWY 7 Check comp~ler felhngs 1 Marco Figura 1.3. La ventana Edit To-Do Item puede usarse para modificar un elemento de tarea pendiente, una operacion que tambien puede realizarse directamente en el codigo fuente. Finalmente, en la ventana TO-DO List se puede elirninar la marca de un ele- mento para indicar que se ha completado. El comentario del codigo fuente cam- biara de T O D O a DONE. Tambien se puede cambiar manualmente el comentario en el codigo fuente. Uno de 10s elementos mas potentes de esta arquitectura es la ventana principal TO-DO List, que puede recopilar automaticamente informacion de tareas pendien- tes a partir de archivos de codigo fuente a medida que se escribe, ordenarla, filtrarla y esportarla a1 Portapapeles como texto simple o una tabla HTML. To- das estas opciones estan disponibles en el menu de contesto. Mensajes ampliados del com pilador y resultados de busqueda en Delphi 7 De manera predeterminada aparece una pequeiia ventana Messages bajo el editor, muestra tanto 10s mensajes del compilador como 10s resultados de las busquedas. Esta ventana se ha modificado de manera importante en Delphi 7. En primer lugar, 10s resultados de busqueda se muestran en una pestafia distinta para que no se mezclen con 10s mensajes del compilador como solia suceder. En segun- do lugar, cada vez que se realiza una busqueda distinta se puede pedir que Delphi muestre 10s resultados en una pagina diferente, para que 10s resultados de la operacion de busqueda anterior sigan disponibles: Se pueden utilizar las combinaciones Alt-Av Pag y Alt-Re Pag para recorrer de manera ciclica las pestaiias de esta ventana. (Los mismos comandos sirven para otras vistas con pestaiias.) Si suceden errores de compilador, puede activarse otra ventana nueva median- te el comando View>Additional Message Info. A medida que se compila un programa, esta ventana Message Hints proporcionara informacion adicional para algunos mcnsajes de error frecuentes, proporcionando sugerencias sobre como solucionar estos errores: Este tipo de ayuda esta destinada mas a programadores novatos, per0 podria ser practico tener presente esta ventana. Es importante darse cuenta de que esta informacion cs bastante facil de personalizar: un director de desarrollo de un proyccto pucdc introducir descripciones apropiadas de errores comunes en un formulario que signifiquen algo especifico para nuevos desarrolladores. Para ha- cer esto, siga las instrucciones del archivo que guarda los parametros de esta caracteristica, el archivo msginfo70,ini que se encuentra en la carpeta b i n de Delphi. El editor de Delphi Aparentemente el editor de Delphi no ha cambiado mucho en la version 7 del IDE. Sin embargo, en el fondo, se trata de una herramienta completamente nueva. Ademas de emplearlo para trabajar con archivo escritos en lenguaje Pascal orien- tad0 a objetos (o-en lenguaje Delphi, como prefiere llamarlo ahora Borland), se puede usar ahora para trabajar con otros archivos relacionados con el desarrollo en Delphi (como archivos SQL, XML, HTML y XSL), al igual que con archivos de otros lenguajes (entre 10s que se incluyen C++ y C#). La edicion de XML y HTML ya estaba disponible en Delphi 6, per0 10s cambios en esta version son importantes. Por ejemplo, durante la edicion de un archivo HTML se tiene sopor- te tanto para resaltado de sintaxis como para acabado de codigo. Las configuraciones el editor para cada archivo (incluido el comportamiento de teclas como Tab) dependen de la estension del archivo que se abra. Se pueden configurar estos parametros mediante la nueva pagina Source Options del cua- dro de dialogo Editor Properties, que muestra la figura 1.4. Esta caracteristica se ha ampliado y abierto aun mas para que incluso pueda configurarse el editor mediante un DTD para formatos de archivo basados en XML o mediante un asistente personalizado que proporcione el resaltado de sintaxis para otros len- guajes de programacion. Otra caracteristica del editor, las plantillas de codigo, son ahora especificas del lenguaje (las plantillas predefinidas para Delphi tendran poco sentido en HTML o C # ) . Figura 1.4. Los diversos lenguajes soportados por el IDE de Delphi se pueden asociar con varias extensiones de archivo mediante la pagina Source Options del cuadro de dialogo Editor Properties. NOTA: C# es el nuevo lenguaje que present6 Micmsofk junto con su arqui- tectura .NET. Borland espera soportar C# en su propio entorno .NET, que actualmente tiene el nombre en codigo de Galileo. Si solo se considera el lenguaje Delphi, el editor incluido en el IDE no ha cambiado mucho en las versiones recientes. Sin embargo, tiene unas cuantas ca- racteristicas que muchos programadores de Delphi desconocen y no utilizan, asi que se merece un poco de analisis. El editor de Delphi nos permite trabajar con varios archivos a la vez, usando una metafora de "bloc de notas con fichas". Se pasa de una ficha del editor a la siguiente pulsando Control-Tab (o Mayus-Control-Tab para movernos en la direccion opuesta). Podemos pasar de una ficha del editor a la siguiente pulsando Control-Tab (o Mayus-Control-Tab para movernos en la direccion opuesta). Se puede arrastrar y soltar las solapas con 10s nombres de unidad situadas en la parte superior del editor para cambiar su orden, para que se pueda usar un simple Control-Tab para moverse entre las unidades en que se trabaje en un momento dado. El menu local del editor posee tambien un comando Pages, que lista todas las fichas dispo- nibles en un submenu, muy util cuando se cargan muchas unidades. Tambien se pueden abrir varias ventanas del editor, cada una de ellas con multiples fichas o pestaiias. Hacer esto es la unica manera de inspeccionar el codigo fuente de dos unidas a la vez. (Realmente, cuando es necesario comparar dos unidades de Delphi, tambien se puede utilizar Beyond Compare, una herra- mienta de comparacion de archivos muy barata y maravillosa escrita en Delphi y disponible a traves de www.scootersoftware.com.) En el cuadro de dialogo Editor Properties, hay algunas opciones que afectan a1 editor. Sin embargo, para definir la propiedad AutoSave del editor, que guarda 10s archivos del codigo fuente cada vez que se ejecuta el programa (y evita que se pierdan 10s datos en caso de que el programa sufra daiios importantes en el depu- rador), tenemos que ir a la ficha Preferences del cuadro de dialogo Environment Options. El editor de Delphi proporciona muchos comandos, incluyendo algunos que se remontan a sus ancestros de emulacion de WordStar (de 10s primeros compiladores Turbo Pascal). No vamos a comentar 10s distintos parametros del editor, ya que son bastante intuitivos y estan bien descritos en la ayuda disponible. Aun asi, fijese en que la pagina de ayuda que describe 10s atajos de teclado es accesible de una sola vez solo si se busca el elemento del indice shortcuts. I TRUCO: Un tmco que debemos recordar es que emplear ias 6rdenes Cut y paste no cs la &ca forma de mover el cb&o &to,, siop qqe fambi6n podemos s e l ~ i o n a r y pnastrar las palabras, expresicihes o lineas enteras de c6dig0, ademis de wpiar el texto en lugar de myerlo, mantmiendo pulsada Ia tecla Cpatrel tnbh-as iir~astnmas. El Code Explorer La ventana Code Explorer, que por lo general esta anclada en el lateral del editor, lista sencillamente todos 10s tipos, variables y rutinas definidas en una unidad, mas otras unidades que aparecen en sentencias u s e s .En el caso de tipos complejos, como las clases, el Code Explorer puede listar informacion pormenorizada, como una lista de campos, propiedades y metodos. Cuando co- menzamos a teclear en el editor, toda la informacion se actualizara. Podemos usar el Code Explorer para desplazarnos por el editor. A1 hacer doble clic sobre una de las entradas del Code Explorer, el editor pasa a la declaracion correspondiente. Tambien podemos modificar nombres de variables, propiedades y m6todos directamente en el Code Explorer. Sin embargo, si se desea utilizar una herramienta visual para trabajar con las clases, ModelMaker ofrece muchas mas caracteristicas. Aunque todo esto resulta bastante obvio a 10s cinco minutos de comenzar a usar Delphi, algunas caracteristicas del Code Explorer no se pueden utilizar de una forma tan intuitiva. Lo importante es que el usuario tiene control total sobre el mod0 en que aparece dispuesta la informacion y que se puede reducir la profun- didad del arbol que aparece en esta ventana cuando se personaliza el Code Explorer. Si reducimos el arbol, podremos realizar las elecciones con mayor rapidez. Podemos configurar el Code Explorer mediante la pagina de Environment Options correspondiente, como se muestra en la figura 1.5. Figura 1.5. Se puede configurar el Code Explorer mediante el cuadro de dialogo Environment Options. Fijese en que a1 eliminar la seleccion de uno de 10s elementos de Explorer Categories situados en la parte derecha de esta pagina del cuadro de dialogo, el Explorer no elimina 10s elementos correspondientes, simplemente aiiade el nodo a1 arbol. Por ejemplo, si se elimina la seleccion de la casilla Uses, Delphi no oculta la lista de unidades usadas; a1 contrario, las unidad usadas aparecen en la lista como nodos principales en lugar de permanecer en la carpeta Uses. Es una buena idea eliminar la seleccion de Types, Classes y VariablesIConstants. Dado que cada elemento del arbol Code Explorer tiene un icono que indica su tipo, la organizacion por campo y metodo parece menos importante que la organi- zacion por especificador de acceso. Es preferible mostrar todos 10s elementos en un grupo unico, puesto asi no es necesario pulsar el raton tantas veces para llegar a cada uno de 10s elementos. En realidad, la posibilidad de seleccionar elementos en el Code Explorer supone una forma muy comoda de desplazarnos por el codigo fuente de una unidad amplia. Cuando hacemos doble clic sobre un metodo en el Code Explorer, el foco se desplaza a la definicion de la declaracion de clase (en la parte de interfaz de la unidad). Se puede usar la combinacion Con- trol-Mayus junto con las teclas de cursor arriba y abajo para saltar de la defini- cion de un metodo o procedimiento en la parte de interfaz de una unidad a su definicion completa en la parte de implernentacion o volver hacia atras (es lo que se llaman Module Navigation). . - NOTA: Algunas de las categorias del explorador que aparecen en la figura 1.5 son mas utilizadas por el Project Explorer que por el Code Explorer. Entre estas se encuentran las opciones de agrupamiento vi r t ua 1 s, !d e Introduced. Exploracion en el editor Otra caracteristica del editor es la Tooltip symbol insight (Ventanas de suge- rencia sobre simbolos). A1 mover el raton sobre un simbolo del editor, una venta- na de sugerencia nos mostrara el lugar en el que se declara el identificador. Esta caracteristica puede resultar especialmente importante para realizar el seguimien- to de identificadores, clases y funciones de una aplicacion que estamos escribien- do y tambien para consultar el codigo fuente de la biblioteca de componentes visuales (VCL). - ADVERTENCIA: Aunque pueda parecer buena idea en principio, no po- demos usar la ventana de sugerencia sobre simbolos para averiguar que unidad declara un identificador que queremos emplear. En realidad. la ven- tana de sugerencia no aparece, si no se ha incluido todavia la unidad corres- pondiente. Sin embargo, la autentica ventaja de esta funcion, es que el usuario puede transformarla en un instrumento auxiliar para desplazarse. Si mantenemos pulsa- da la tecla Control y movemos el raton sobre el identificador, Delphi creara un enlace activo con la definicion, en lugar de mostrar la ventana de sugerencia. Dichos enlaces aparecen en color azul y estan subrayados, estilo tipico de 10s exploradores Web, y el punter0 se transforma en una mano siempre que se situa sobre el enlace. Podemos, por ejemplo, pulsar Control y hacer clic sobre el identificador TLabel para abrir su definition en el codigo de la VCL. Cuando seleccionamos referencias, el editor conserva la pista de las diversas posiciones a las que se ha movido y gracias a ella podemos pasar de una referencia a otra (de nuevo como en un explorador Web mediante 10s botones Browse Back y Browse Forward que se encuentran en la esquina superior derecha de las ventanas o mediante las combi- naciones Alt-Flecha izda. o Alt-Flecha dcha.). Tambien podemos hacer clic so- bre las flechas desplegables proximas a los botones Back y Forward para ver una lista pormenorizada de las lineas de 10s archivos de codigo fuente a las que ya hemos accedido, para tener mayor control sobre 10s movimientos adelante y atras. Ahora cabe preguntarse como podemos saltar directamente al codigo fuente de la VCL si no forma parte de nuestro proyecto. El editor no solo puede encontrar las unidades de la ruta de busqueda (Search, que se compila como parte del proyecto), sino tambien aquellas que estan en las rutas Debug Source, Browsing y Library de Delphi. La busqueda se realiza en estos directorios en el mismo orden en que aparecen aqui enumerados y podemos definirlos en la ficha Directories/Conditionals del cuadro de dialogo Project Options y en la ficha Library del cuadro de dialogo Environment Options. Por defecto, Delphi aiiade 10s directorios de codigo fuente de la VCL a la ruta Browsing del entorno. Class Completion El editor de Delphi tambien puede generar parte del codigo fuente, completan- do lo que ya se haya escrito. Esta caracteristica se llama Class Completion, y se activa a1 pulsar la combinacion de teclas Control-Mayus-C. Aiiadir un controla- dor de eventos a una aplicacion es una operacion rapida, porque Delphi aiiade automaticamente la declaracion de un nuevo metodo que controle el evento y nos proporciona el esquema del metodo en la seccion de implementacion de la unidad. Esto forma parte del soporte para programacion visual de Delphi. De un mod0 similar, las ultimas versiones de Delphi han conseguido facilitar el trabajo de 10s programadores que escriben codigo extra detras de 10s controladores de evento. De hecho, la nueva caracteristica de creacion de codigo afecta a 10s metodos generales, a 10s metodos de control de mensajes y a las propiedades. Por ejemplo, si tecleamos el siguiente codigo en la declaracion de clase: public procedure Hello (MessageText : string) ; y a continuacion, pulsamos Control-Mayus-C, Delphi nos ofrecera la defini- cion del metodo en la parte de implementacion de la unidad y crea las siguientes lineas de codigo: { T F o r m l ) procedure TForml.Hello(MessageText: string); begin end; Esto resulta mas comodo que copiar y pegar una o mas declaraciones, aiiadir 10s nombres de clase y por ultimo duplicar el codigo begin. . . end en cada metodo copiado. La funcion C 1 as s C omp 1 e t i o n tambien puede funcionar a la inversa: podemos escribir la implementacion del metodo directamente con su codigo y despues pulsar Control-Mayus-C para crear la entrada necesaria en la declaracion de clase. El ejemplo mas importante y util de esta funcion de completitud de clases es la generacion automatica de codigo para dar soporte a las propiedades declaradas en las clases. Por ejemplo, si en una clase se escribe p r o p e r t y Value: I n t e g e r ; y se pulsa Control-Mayus-C, Delphi convertira la linea enp r o p e r t y Value: I n t e g e r r ead fValue w r i t e Se tva lue ; Delphi aiiadira tambien el metodo setvalue a la declaracion de clase y proporcionara una implementacion predefinida para ese metodo. Code Insight Ademas del Code Explorer, la funcion de completitud de clases y las funcio- nes de desplazamiento, el editor de Delphi soporta la tecnologia Code Insight. En conjunto, las tecnicas Code Insight se basan en un analisis sintactico continuo en segundo plano, tanto del codigo fuente que escribimos como del codigo fuente de las unidades del sistema a las que se refiere nuestro codigo. La funcion Code Insight implica cinco capacidades: Code Completion, Code Templates, Code Parameters, Tooltip Expression Evaluation y Tooltip Symbol Insight. Esta ultima caracteristica se trato durante la seccion sobre la exploracion en el editor. Todas estas caracteristicas se pueden habilitar, inhabilitar y configu- rar en la pagina Code Insight del cuadro de dialog0 Editor Properties. Code Completion La funcion Code Completion permite escoger la propiedad o metodo de un objeto simplemente buscandolo en una lista o escribiendo sus letras iniciales. Para activar esta lista, solo hay que teclear el nombre de un objeto, como But tonl, aiiadir el punto y esperar. Para que forzar la aparicion de la lista, hay que pulsar Control-Barra espaciadora; para quitarla cuando no queramos verla, hay que pulsar Esc. La funcion Code Completion permite ademas buscar un valor adecua- do en una sentencia de asignacion. Cuando comenzamos a teclear, la lista va filtrando su contenido de acuerdo con la parte inicial del elemento que hemos escrito. La lista Code Completion emplea colores y muestra mas detalles para ayudarnos a distinguir elementos diferentes. En Delphi, se pueden personalizar estos colores mediante la pagina Code Insight del cuadro de dialogo Editor Properties. Otra caracteristica en el caso de funciones con parametros es la inclusion de parentesis en el codigo creado y la aparicion inmediata de la ventana de sugerencia de la lista de parametros. Cuando se escribe : = despues de una variable o propiedad, Delphi listara todas las demas variables u objetos del mismo tipo, ademas de 10s objetos que tengan propiedades de ese tipo. Mientras la lista permanece visible, podemos hacer clic con el boton derecho del raton sobre ella para modificar el orden de 10s elementos, clasificandolos por alcance o por nombre y tambien podemos adaptar el tamaiio de la ventana. Desde Delphi 6, Code Completion funciona ademas en la parte de interfaz de una unidad. Si pulsamos Control-Barra espaciadora mientras el cursor esta dentro de la definition de clase, obtendremos una lista de 10s metodos virtuales que se pueden sobrescribir (como por ejemplo, 10s metodos abstractos), 10s meto- dos de las interfaces implementadas, las propiedades de clase basica y, por ulti- mo, 10s mensajes del sistema que se pueden controlar. A1 seleccionar uno de ellos, aiiadiremos sencillamente el metodo adecuado a la declaracion de clase. En este caso concreto, la lista Code Completion permite la seleccion multiple. TRUCO: Si el c6digo que hemos escrito es incorrecto, Code Insight no funcionari y veremos un mensaje de error generic0 que nos indica dicha situation. Es posible hacer que aparezcan errores especificos de Code Insight 1, porque no se abre ilacion). Para activar esta caracteristicas, es necesarlo estamecer una entrada del Registro no en el panel Message (que debera estar ya abiertc automaticamente para mostrar 10s errores de comp . , .. . .. - documentada, defhendo la clave de cadena \Delphi \ 7 . 0 \Cornpi 1 ing\ ShowCodeInsiteErrors con el valor 1. Hay algunas caracteristicas avanzadas de la funcion Code Completion que no resultan faciles de ver. Una particularmente util esta relacionada con el descubri- miento de 10s simbolos en unidades no utilizadas por nuestro proyecto. Cuando recurrimos a ella (con Control-Barra espaciadora) sobre una linea en blanco, la lista incluye tambien simbolos de unidades comunes (como Math, StrUtils y DateUtils) todavia no incluidas en la sentencia uses de la unidad en uso. A1 seleccionar uno de estos simbolos externos, Delphi aiiade la unidad a la sentencia uses de forma automatics. Esta caracteristica (que no funciona dentro de expresiones) esta dirigida por una lista de unidades adicionales que puede ser personalizada, almacenada en la clave de registro \Delphi\7.0\CodeCompletion\ExtraUnits. TRgC(r:,l&fpG 7 b capaoidad de explorar la d e c h r w i h de ele- mentos de la fista.de campletitud de codigo a1 mantener pul'sadd la teela Control y'hacer chc sohre cualquier identificador de la Ikta. L . Code Templates Esta caracteristica permite insertar una de las plantillas de codigo predefinidas, como una declaracion compleja con un bloque interior b e g i n . . . . e n d . Las plantillas de codigo deben activarse de forma manual, usando Control-J para obtener una lista de todas ellas. Si tecleamos unas cuantas letras (como una pala- bra clave) antes de pulsar Control-J, Delphi listara solo las plantillas que comiencen por dichas letras. Tambien se pueden aiiadir plantillas de codigo personalizadas, para crear me- todos abreviados para 10s bloques de codigo que usemos normalmente. Por ejem- plo, si empleamos con frecuencia la funcion MessageDlg , podemos aiiadir una plantilla para la misma. Para modificar plantillas, mediante la pagina Source Options del cuadro de dialogo Editor Options, hay que seleccionar Pascal en la lista Source File Type y hacer clic sobre el boton Edit Code Templates. A1 hacer esto, aparecera el nuevo cuadro de dialogo Code Templates de Delphi 7. En este momento, si hacemos clic sobre el boton Add, escribimos un nuevo nombre de plantilla (por ejemplo, d e s o r d e n ) , escribimos tambien una descrip- cion y, a continuacion, aiiadimos el siguiente texto a1 cuerpo de la plantilla en el control memo Code: MessageDlg ( ' I ' , mtInformation, [mbOK] , 0 ) ; Ahora, cada vez que necesitemos crear un cuadro de dialogo de mensaje, sim- plemente escribiremos d e s o r d e n y, a continuacion, pulsaremos Control-J para obtener el texto completo. El caracter de linea vertical indica la posicion dentro del codigo fuente en la que estara el cursor en el editor despues de haber desplega- do la plantilla. Deberiamos escoger la posicion en la que queremos comenzar a teclear para completar el codigo producido por la plantilla. Aunque pueda parecer que las plantillas de codigo, a primera vista, se corres- ponden con palabras clave del lenguaje, estas son en realidad un mecanismo mas general. Se guardan en el archivo DELPHI32.DC1, un archivo de texto en un formato bastante simple que puede editarse con facilidad. Delphi 7 tambien per- mite exportar la configuracion para un lenguaje a un archivo e importarla, lo que facilita que 10s desarrolladores intercambien sus propias plantillas personalizadas. Code Parameters La funcion Code Parameters muestra, en una ventana de sugerencia, el tip0 de datos de 10s parametros de un metodo o funcion mientras 10s tecleamos. A1 escri- bir el nombre de la funcion o metodo y abrir el parentesis, apareceran inmediata- mente 10s nombres y tipos de parametro en una ventana de sugerencia contextual. Para que forzar a que aparezcan 10s parametros de codigo, podemos pulsar Con- trol-Maylis-Barra espaciadora. Ademas, el parametro en uso aparece resaltado en negrita. Tooltip Expression Evaluation La funcion Tooltip Expression Evaluation es una caracteristica en tiempo de depuracion. Muestra el valor del identificador, la propiedad o expresion que esta bajo el cursor del raton. En el caso de una expresion, normalmente necesitara seleccionarla en el editor y despues mover el cursor sobre el texto seleccionado. Mas teclas de metodo abreviado del editor El editor tiene muchas mas teclas de metodo abreviado que dependen del estilode editor escogido. A continuacion, aparece una lista de las menos conocidas: Control-Mayus mas una tecla numerica del 0 a1 9 activa un marcador, indicado en el margen "para encuadernacion" del lateral del editor. Para volver a1 marcador, pulsamos la tecla Control mas la tecla numerica. La utilidad de 10s marcadores en el editor esta limitada por el hecho de que un nuevo marcador puede sobrescribir a otro ya existente y porque 10s marca- dores no son permanentes (se pierden cuando se cierra el archivo). Control-E activa la busqueda incremental. Hay que pulsar Control-E y teclear directamente la palabra que queramos buscar, sin necesidad de pasar por un cuadro de dialogo especial ni de hacer clic sobre la tecla Enter para realizar la busqueda. Control-Mayus-I sangra diversas lineas de codigo a1 mismo tiempo. El numero de espacios utilizado es el establecido en la opcion Block Indent de la ficha Editor del cuadro de dialogo Environment Options. Control- Mayus-U es la combinacion correspondiente para deshacer el sangrado del codigo. Control-O-U cambia el codigo seleccionado a mayusculas o minusculas. Tambien se puede usar Control-K-E para cambiar a minuscula y Con- trol-K-F para pasar a mayuscula. Control-Mayus-R inicia la grabacion de una macro, que mas tarde se puede reproducir utilizando la combinacion de teclas Control-Mayus-P. La macro graba todas las operaciones de escritura, movimiento y borrado realizadas en el archivo del codigo fuente. A1 reproducir la macro simple- mente se repite la secuencia. Las macros del editor resultan bastante utiles para repetir operaciones que constan de varios pasos, como volver a dar formato a1 codigo fuente u organizar 10s datos de una manera mas legible en el mismo. Si se mantiene pulsada la tecla Alt, se puede arrastrar el raton para selec- cionar zonas rectangulares del editor, no solo lineas consecutivas y pala- bras. Vistas que se pueden cargar En la ventana del editor ha habido otra modificacion importante, presentada en Delphi 6. Para cada uno de 10s archivos que se cargan en el IDE, el editor puede mostrar ahora diversas vistas que podemos definir de forma programada y aiiadir a1 sistema, y despues cargar para unos archivos determinados. La vista mas utilizada es la pagina Diagram, que ya estaba disponible en 10s modulos de datos de Delphi 5, aunque era menos potente. Existe otro conjunto de vistas disponibles en las aplicaciones Web, entre las que se encuentra una vista HTML Script, una vista previa HTML Result y muchas mas. Se pueden utilizar las combinaciones Alt-Av Pag y Alt-Re PBg para recorrer las pestaiias inferiores de este editor; con Control-Tab se salta entre las paginas (o archivos) que se muestran en las pestaiias superiores. Diagram View La vista de diagrama muestra las dependencias entre componentes, como las relaciones padrelhijo, de posesion, las propiedades enlazadas y las relaciones genericas. En el caso de componentes de un conjunto de datos, tambien soporta relaciones maestroldetalle y conexiones de busqueda. Podemos incluso aiiadir comentarios en bloques de texto enlazados a componentes especificos. El diagrama no se crea de forma automatica. Debemos arrastrar 10s compo- nentes desde la vista en arb01 a1 diagrama, en el que automaticamente apareceran las relaciones entre 10s mismos. Podemos seleccionar diversos elementos desde la Object TreeView y arrastrarlos todos a la vez a la ficha Diagram. Lo agradable es que podemos definir propiedades simplemente dibujando fle- chas entre 10s componentes. Por ejemplo, despues de mover un control Edit y una etiqueta a1 diagrama, podemos seleccionar el icono Property Connector, hacer clic sobre la etiqueta y arrastrar el cursor del raton sobre el control Edit. Cuando soltemos el boton del raton, el diagrama establecera una relacion de posesion basada en la propiedad FocusCont r o 1, que es la unica propiedad de la etique- ta que se refiere a un control Edit. Esta situacion se muestra en la figura 1.6. Como se puede ver, la definicion de propiedades es direccional: si arrastramos la linea de relacion de propiedad desde el control Edit a la etiqueta, en realidad, estamos intentando usar la etiqueta como valor de una propiedad del cuadro de edicion. Dado que eso no es posible, veremos un mensaje de error que nos indica el problema y nos ofrece la posibilidad de conectar 10s componentes en la direc- cion opuesta. El Diagram View nos permite crear varios diagramas para cada unidad Delphi (es decir, para cada formulario o modulo de datos). Simplemente se proporciona un nombre a1 diagrama y se puede aiiadir tambien una descripcion, haciendo clic sobre el boton New Diagram, se prepara otro diagrama y se puede pasar de un diagrama a otro usando el cuadro combinado de la barra de herramientas de la vista en diagrama. -- ti- h e Q+fcrolim Ths IS a simple version J Figura 1.6. La vista Diagram rnuestra relaciones entre cornponentes (e incluso perrnite establecer esas relaciones). Aunque se pueda emplear la vista en diagrama para establecer relaciones, su funcion principal es documentar nuestro diseiio. Por esa razon: es importante que se pueda imprimir el contenido de dicha vista. Al usar la orden estandar File>Print mientras este activada la vista en diagrama, Delphi nos indica que seleccionemos las opciones para personalizar la impresion, como se puede ver en la figura 1.7. M base r- Figura 1.7. Las opciones de impresion de la vista en diagrama. La informacion de la vista Diagram se guarda en un archivo separado, no como parte del archivo DFM. Delphi 5 empleaba 10s archivos de informacion en tiempo de diseiio (DTI), que tenian una estructura similar a 10s archivos INI. Delphi 6 y 7 todavia pueden leer el antiguo formato .DTI, per0 usan el nuevo formato Delphi Diagram Portfolio (.DDP). Estos archivos utilizan aparentemente el formato binario DFM (o uno simi- lar), por lo que se pueden editar como texto. Obviamente todos estos archivos son inutiles en tiempo de ejecucion (no tiene sentido incluirlos en la compilacion del archivo ejecutable). NOTA: Si se desea experimentar con la vista en diagrama, se puede co- menzar abriendo el proyecto DiagramDemo. El formulario del programa +:,,, A,, A: ,,,, -,- ,,,,:,J,-. ..,, ,- ,I .I- 1, C ,.., , 1 L ...-,, ,..,I.- ,A, LIGIIG UW3 U l i l g l i U l l i l 3 i l?~UblilUWS. U l l U GI1 GI UG lil I l g U l i l l .V Y U l l U 1IIUC;llU IllilJ complejo con un menu desplegable y sus elementos. Form Designer Otra ventana de Delphi con la que vamos a interactuar muy a menudo es el Form Designer; una herramienta visual para colocar componentes en 10s formu- larios. En el Form Designer, se puede seleccionar directamente un componente con cl raton o a traves dcl Object Inspector o la Object Treeview, mttodos ultimos utiles en caso de quc un control quede oculto. Si un control cubre a otro por complete, se puedc emplear la tecla Esc para seleccionar el control padre del control actual. Se pucdc pulsar la tecla Esc una o mas veces para seleccionar el formulario o pulsar y mantener pulsada la tecla Mayus mientras sc hace clic sobre cl componcntc scleccionado. Esto desactivara la selection del componente en uso y seleccionara el formulario por defecto. Esisten dos alternativas al uso del raton para fijar la posicion de un compo- ncnte. Se pueden definir valores para las propiedades L e f t y TOP, o bien usar las teclas de cursor mientras se mantiene pulsada la tech Control. El uso de las teclas de cursor resulta util sobre todo para precisar la posicion de un elemento (cuando la opcion Snap To Grid se encuentra activada), a1 igual que mantener pulsada la tecla Alt y utilizar el raton para mover el control. Si se pulsa la combinacion Control-Mayus junto con una tecla de cursor, el componente se mover6 solo a intervalos de cuadricula. Del mismo modo, a1 pulsar las teclas de cursor mientras se mantiene pulsada la teclaMayus, podemos precisar el tamaiio de un componente, algo que tambien se puede hacer mediante la tecla Alt y el raton. Para alinear diversos componentes o hacer que tengan el mismo tamaiio, se pueden sclcccionar todos ellos y establecer las propiedades Top, L e f t , Width o H e i g h t de todos a1 mismo tiempo. Para seleccionar varios componentes, po- demos haccr clic sobre ellos con el raton mientras mantenemos pulsada la tecla Mayus o. si todos 10s componentes se encuentran en zonas rectangulares, se puede arrastrar el raton hasta "dibujar" un rectangulo que 10s rodee. Para selec- cionar 10s controles hijo (por ejemplo, 10s botones que se encuentran dentro de un panel), arrastraremos el raton dcntro del panel mientras que mantendremos pulsa- da la tecla Control, ya que de no ser asi desplazaremos el panel. Cuando ya esten seleccionados 10s diversos componentes, tambien se puede fijar su posicion rela- tiva utilizando el cuadro de dialog0 Alignment (con la ordcn A l i g n del menu de metodo abreviado del formulario) o Alignment Palette (a la que accedemos mediante la orden del menu View>Alignment Palette). Cuando este terminado el diseiio de un formulario, podemos emplear la orden Lock C o n t r o l s del menu Edit para evitar cambiar por equivocacion la posi- cion de una componente en un formulario. Esto resulta util, sobre todo teniendo en cuenta que las operaciones Undo en 10s formularios son limitadas (solo se puede recuperar elementos eliminados), per0 la definicion no es permanente. Entre otras de sus caracteristicas, el Form Designer ofrece diversas ventanas de sugerencia: A1 mover el punter0 sobre un componente, en la sugerencia aparece el nombre y el tipo del componente. Desde la version 6, Delphi ofrece suge- rencias extendidas, con datos sobre la posicion del control, el tamaiio, el orden de tabulacion y mas. Esta es una mejora de la configuracion del entorno Show Component Captions que se puede mantener activada. Cuando adaptamos el tamaiio de un control, en la sugerencia aparece el tamaiio actual (las propiedades Wid th y H e i g h t ) . Por supuesto, estas caracteristicas estan disponibles solo para controles, no para componentes no visuales (que estan indicados en el Form Designer mediante iconos). A1 mover un componente, la sugerencia indica la posicion actual (las pro- piedades L e f t y Top). Por ultimo, se pueden guardar 10s archivos DFM (Delphi Form Module, Mo- dulo de Formulario Delphi) en el formato de recurso binario tradicional, en lugar de hacerlo como texto normal que es el comportamiento predeterminado. Esta opcion se puede modificar en el caso de un formulario individual, con el menu de metodo abreviado del Form Designer o establecer un valor predefinido para 10s formularios nuevos que creemos en la ficha Designer del cuadro de dialogo Environment Options. En la misma ficha, podemos especificar tambien si 10s formularios secundarios de un programa se crearan automaticamente a1 arrancar, una decision que siempre se podra modificar en el caso de cada formulario indivi- dual (usando la ficha Forms del cuadro de dialogo Project Options). Disponer de archivos DFM almacenados como texto permite trabajar de mane- ra mas eficaz con sistemas de control de versiones. Los programadores no se aprovecharan mucho de esta caracteristica, ya que se podria simplemente abrir el archivo DFM binario en el editor de Delphi con un comando especifico desde el menu de metodo abreviado del diseiiador. Por otra parte, 10s sistemas de control de versiones necesitan guardar la version textual de 10s archivos DFM para ser capaz de compararlos y extraer las diferencias entre dos versiones del mismo archivo. En cualquier caso, si se utilizan archivos DFM como texto, Delphi 10s convertira a un formato de recurso binario antes de incluirlos en el archivo ejecu- table del programa. Los archivos DFM estan enlazados a su ejecutable en forma- to binario para reducir el tamaiio del archivo ejecutable (aunque no esten comprimidos) y para mejorar el rendimiento en tiempo de ejecucion (se pueden cargar mas rapido). NOTA: Los archivos de texto DPM resultan m b faciles de tramportar de una version a otra de Delphi que sus versiones binarias. Aunque una ver- sion mas antigua de Delphi puede no acevtar una nueva propiedad de un - control en un archivo DFM ireado por u& veni6n posterior be Delphi, la 1 version anterior si sera capaz de leer el resto del archivo de texto DFM. Sin a,t...,,, A I, ..,,..:L.. ,A- c~.-.:~,+, A, r\-1-L: -Z.-.Ar. .... ....-.., +:,, A, A,+,.. GuIualgu, SI la YGIJIUU 111aa IGUGULG UG U G I ~ L U mau~ u u IIUGVU c~yu UG uacua, la version anterior no podra leer en absoluto 10s archivos binarios DFM mas recientes. hcluso aunque no suene probable, recuerde que 10s sistemas que funcionan con 64 bits e s t b a la vuelta de la esquina. Si tiene dudas, guarde 10s archivos en formato texto DFM. Fijese tarnbien en que todas las versiones de Delphi soportan DFM en formato texto, usando la herrarnienta en linea de comandos convert que se encuentra en el directorio bin. Por ultimo, tenga presente que la biblioteca CLS utiliza la extension XFM en lugar de la extension DFM, tanto en Delphi como en Kylix. Object lnspector Para visualizar y modificar las propiedades dc 10s componentes de un formula- rio (u otro disciiador) en tiempo de diseiio, se puede utilizar el Object Inspector. En comparacion con las primeras versiones de Delphi, el Object lnspector dis- pone de unas cuantas caracteristicas nuevas. La ultima, presentada en Delphi 7, es cl uso de una fuente en negrita para resaltar las propiedades que tienen un valor distinto dcl predefinido. Otro importante cambio (en Delphi 6) es la capacidad del Object lnspector de desplegar las referencias de 10s componentes emplazados. Las propiedades que se refieren a otros componentes aparecen en un color dife- rcntc y pueden desplegarse seleccionado el simbolo + de la izquierda, como ocu- rre con 10s subcomponentes internos. A continuacion, se pueden modificar las propiedades de ese otro componente sin tener que seleccionarlo. La siguiente figura muestra un componente conectado (un menil desplegable) expandido en el Object lnspector mientras que se trabaja con otro componente (un cuadro de lista): Esta caracteristica de ampliacion de la interfaz tambien soporta subcompo- nentes, tal y como demuestra el nuevo control LabeledEdit. Una caracteristi- ca relacionada del Object lnspector es que podemos seleccionar el componente a1 que hace referencia una propiedad. Para ello, hacemos doble clic sobre el valor de la propiedad con el boton izquierdo del raton mientras mantenemos pulsada la tecla Control. Por ejemplo, si tenemos un componenteMainMenu en un formu- lario y estamos echando un vistazo a las propiedades del formulario en el Object Inspector, podemos seleccionar el componente MainMenu moviendonos a la pro- piedad MainMenu del formulario y haciendo doble clic sobre el valor de dicha propiedad mientras mantenemos pulsada la tecla Control. Con esto se selecciona el menu principal indicado junto con el valor de la propiedad en el Object Ins- pector. A continuacion aparece una lista de algunos cambios recientes del Object Inspector: La lista situada en la parte superior del Object Inspector muestra el tip0 de objeto y permite escoger un componente. Puede eliminarse para ahorrar algo de espacio, ya que se puede seleccionar componentes en la Object Treeview. Las propiedades que hacen referencia a un objeto son ahora de un color diferente y pueden ampliarse sin cambiar la seleccion. Opcionalmente se pueden ver tambien las propiedades de solo lectura en el Object Inspector. Por supuesto, estan en gris. El Object lnspector posee un cuadro de dialog0 Properties, que permite personalizar 10s colores de diversos tipos de propiedades y el comporta- miento general de esta ventana. Desde Delphi 5, la lista desplegable de una propiedadpuede incluir ele- mentos graficos. Esta caracteristica la utilizan propiedades como color y Cursor, y es particularmente util para la propiedad Image Index de 10s componentes conectados a una ImageLis t . NOTA: Las propiedades de la interfaz plleden coafigurarse ahora en tiem- po de diseKo utilimdo d meet ImpMor. Este n s a s l linodeloInterfaced Component Reference ('Rdeiencias de G~m~pobetrte por hterfaz) presenta- do en KyIix/Delpbi 6, en &qxk IT& c?mp&&nk$ pueden implemmtar y mantener referenciae* a b interfaces siempre qae Iw ihterfaces eat& impl&entadas por coapmenteJ, Este mbdelb hnciicma d igrlal qae h antiguas y s imp~es . referencb r componentes, p m IBS propidides de interfaz pueden enlazaise c m 4QUier componente que implemente la interfaz necesaria. Las prcjpiahdes #e i n t e e no egt4.11 lhfiitadas a mtip de componentes especjfico (ma elwe o so's clases derivadu). Cuands ha- cemos clic sobm la%&, despggableen el edihr de %& Y n s w i p a r a . - - - . - - - . - - - . . - I obtener una propiedad deinterfaz, aparecen todos 10s componentes de1 for-- 1 mulario actual (y formularios relacionados) que irnpleme& la interfaz. Fuentes desplegables en el Object Inspector El Object Inspector de Delphl tiene una lista grafica desplegable para diversas propiedades. Si queremos aiiadir una que muestre la imagen real de la fuente seleccionada y se corresponda a la subpropiedad Name de la propiedad Fon t , hay que instalar en Delphi un paquete que habilite la variable global FontName PropertyDisplayFontNames de la nue- va unidad VCLEditors. Esta capacidad ya estaba disponible en Delphi, pero quedaba inhabilitada ya que la mayoria de 10s ordenadores tienen instalado un gran numero de fbentes y mostrarlas todas ralentizaria significativamente el ordenador. En el paquete Oi FontPk, que se puede encontrar entre 10s programas de ejemplo, se ha hecho esto. Una vez que se haya instalado dicho paquete. podemos desplazarnos a la propiedad F o n t de cualquier componente y emplear el menu desplegable grafico Name, como se ve a continuacion: B F d ITFant) j Charsel 'DEFAULT-CHARSET 1 Cob ' W cWindowText I Existe una segunda forma, . P . Existe una segunda forma, miis compteja, de personalizar el Object Ins- pector: una fuente personalizada para todo el Object Inspector, para que su texto resulte mhs visible. Esta caracteristica resulta especialmente util en el caso de presentaciones publicas. Categorias propiedades Delphi incluye tambien el concept0 de categorias de propiedades, activadas mediante la opcion Arrange del mcnu local del Object Inspector. Si se activa csta opcion, las propiedades no se listaran alfabeticamente sino que se organiza- ran por grupos, con la posibilidad de que cada propiedad aparezca cn diversos grupos. Las categorias tienen la ventaja de reducir la complejidad del Object Inspec- tor. Se puede usar el submenu View presente en cl menu de metodo abreviado para ocultar propiedades de determinadas categorias. sea cual sea el mod0 en que aparezcan (es decir, incluso aunque se desee el tradicional orden por nombrc, aun asi se podran las propiedades de algunas categorias). Object TreeView Delphi 5 introdujo una vista en arbol para modulos de datos: en la que se podian ver las relaciones entre 10s componentes no visuales, como 10s conjuntos de datos, 10s campos, las acciones, etc. Delphi 6 amplio esta idea a1 proporcionar una Object TreeView para cada diseiiador, como en el caso de 10s formularios simples. La Object TreeView se situa por defecto sobre el Object Inspector. La Object TreeView muestra todos 10s componentes y objetos del formulario en un arbol en el que se representan sus relaciones. La relacion mas obvia es la relacion padrelhijo: si colocamos un panel sobre un formulario, un boton dentro de cste y uno fuera del panel, en el arbol aparece- ran 10s dos botones, uno bajo el formulario y el otro bajo el panel, tal como mucstra la figura: Fijcse en que la vista en arbol esta sincronizada con el Object Inspector y con el Form Designer, de tal mod0 que cuando escogemos un elemento y cam- biamos el foco en una de estas tres herramientas, tambien cambiara en las otras dos . Ademas de la relacion padrelhijo, la Object TreeView muestra tambien otras relaciones, como la de propietariolposeido, componentelsubobjeto, coleccion/ele- mento, y otras especificas como conjunto de datos/conexion y fuente de datosl relaciones del conjunto de datos. A continuacion, se puede ver un ejemplo de la estructura de un menu en forma de arbol: L d h l + + - -- - hn New (Newl) em, Open (Open11 bM, Save (Save1 ) A veces, en la vista en arbol aparecen tambien nodos falsos, que no correspon- den a un objeto real sino a uno predefinido. Como ejemplo de este comportamien- to, si desplegamos un componente Table (desde la ficha BDE), veremos dos iconos en gris que corresponden a la sesion y a1 alias. Tecnicamente, la Object TreeView usa iconos en gris para 10s componentes que no permanecen en tiempo de diseiio. Son componentes reales (en tiempo de diseiio y en tiempo de ejecu- cion), per0 como son objetos predefinidos que estan construidos en tiempo de ejecucion y no contienen datos permanentes que se puedan editar en tiempo de diseiio, el Data Module Designer no nos permite editar sus propiedades. Si colocamos una Table en el formulario, veremos tambien elementos que tienen a su lado una interrogacion en rojo dentro de un circulo amarillo. Este simbolo indica elementos parcialmente definidos. La Object TreeView soporta varios tipos de arrastre: Podemos escoger un componente de la paleta (haciendo clic sobre el, no arrastrandolo), mover el raton sobre el arbol y hacer clic sobre un compo- nente para dejarlo ahi. Esto nos permite dejar un componente en el conte- nedor que corresponda (formulario, panel y otros), aunque su superficie este totalmente cubierta por otros componentes, algo que evita, a su vez, que dejemos el componente en el diseiiador sin reorganizar primer0 10s demas componentes. En la vista en arbol, podemos arrastrar componentes, Ilevandolos, por ejem- plo, de un contenedor a otro. Con el Form Designer, en cambio, solo podemos utilizar la tecnica de cortar y pegar. Mover, en lugar de cortar, nos ofrece la ventaja de conservar las conexiones entre 10s componentes, si las hubiera, y de que no se pierdan como ocurre al eliminar el componente durante la operacion de cortar. Podemos arrastrar 10s componentes desde la vista en arbol a la vista en diagrama. Al pulsar con el boton derecho del raton sobre cualquier elemento de la vista en arbol, aparece un menu de metodo abreviado, similar a1 menu de componentes que obtenemos cuando el componente esta en un formulario (y en ambos casos, en el menu de metodo abreviado pueden aparecer elementos relacionados con 10s editores personalizados de componentes). Podemos incluso eliminar elementos del arbol. La vista en arbol sirve tambien como editor de colecciones, como pode- mos ver a continuacion en el caso de la propiedad Columns de un control Listview. En esta ocasion, no solo podemos reorganizar y eliminar 10s elementos existentes, sin0 tambien aiiadir elementos nuevos a la coleccion. 1 Folrnl 0 Bullon2 [-I IJ - ::, Columns 4 o . r L ~ r c o l w m L: 1 TL~~tCalurnn 2 . TL~stCalumn 4 3 - T L~slCd.mm TRUCO: Se pueden imprimir 10s contenidos de la Object TreeView para documentarse. Sirnplemente hay que seleccionar la ventana y usar la orden File>Print (no existe una orden P r i n t en el menu de metodo abreviado). Secretos de la Component Palette La Component Palette se utiliza para seleccionar componentes que sc de- Sean aiiadir al diseiiador actual. Si movemos el raton sobre un componente, apare- ccra su nombre. En Delphi 7, la sugerencia muestra tambien el nombre de la unidad en que se define el componente. La Component Palette tiene demasiadas pestaiias. Se pueden ocultar laspestaiias que contienen 10s componentes que no se planea utilizar y reorganizar la ventana para que se adecue a las necesidades del momento. En Delphi 7, tambien se pueden arrastrar las pestaiias para reorganizarlas. Mediante la pagina Palette del cuadro de dialog0 Environment Options, se pueden reordenar completamen- te 10s componentes de las diversas paginas, afiadir elementos nuevos o llevarlos de una pagina a otra. Cuando hay demasiadas fichas en la Component Palette, sera necesario moverlas para alcanzar un componente. Esiste un truco muy senci- 110 que se puede usar en este caso: dar un nuevo nombre mas corto a cada ficha, para que todas ellas encajen en la pantalla (es obvio, una vez que se piensa). Delphi 7 ofrece otra caracteristica nueva. Cuando hay demasiados componen- tes en una unica pagina, Delphi muestra una flecha abajo doble; si se hace clic sobre ella se mostrara el resto de 10s componentes sin tener que recorrer la pagina Palette. El menu contextual de la Component Palette tiene un submenu Tabs que muestra todas las paginas de la paleta en orden alfabetico. Se puede utilizar este subrncnu para modificar la pagina activa. en particular cuando la pagina que se neccsita no esta visible en pantalla. L TRUCO: Se puede establecer el orden de las entradas en el submenu Tabs para que tengan el misrno orden que la propia paleta, en lugas de un orden alfabetico. Para hacer esto, hay que ir a la seccion Main Window del Registroi para Delphi (dentro de \Sof tware\Borland\Delphi\7 . 0 para el usuario actual) y dar a la clave Sort Palette Tabs Menu el valor de 0 (falso). Una importante caracteristica no documentada de la Component Palette es la posibilidad de activar un "seguimiento directo". Si configuramos claves especia- les del Registro. podemos seleccionar una ficha de la paleta a1 movernos sobre la solapa, sin tener que hacer clic con el raton. Se puede aplicar esta misma caracte- ristica a las barras de desplazamiento de 10s componentes situadas a ambos lados de la paleta, que aparecen cuando hay demasiados componentes en la ficha. Para activar csta caracteristica oculta, hay que aiiadir una clave Extras dentro de la seccion \HKEY CURRENT USER\Software\Borland\Delphi\7.0. Bajo esta clave ST introducen-dos valores de cadena, Auto Palet teselect y Auto Palette S c r o 11 y definimos cl valor de cada cadcna como '1' Copiar y pegar componentes Una caracteristica interesante del Form Designer es la posibilidad de copiar y pegar componentes de un formulario a otro o de duplicar el componente en el formulario. Durantc dicha operation, Delphi duplica todas las propiedades, man- tiene 10s controladorcs dc cvcntos conectados y, si es necesario, cambia el nombre dcl control (quc dcbc scr unico en cada formulario). Tambien es posible copiar componentes del Form Designer a1 editor y viccversa. Cuando se copia un com- ponente en cl Portapapelcs, Delphi coloca tambien en este su descripcion textual. Se puede incluso editar la version textual de un componente, copiar el texto a1 Portapapeles y luego pegarlo de nuevo en el formulario como un componente nuevo. Por ejemplo, si se coloca un boton sobre un formulario, se copia y luego se pega en un editor (que puede ser el propio editor de codigo de Delphi o cualquier procesador de texto), se obtendra la siguiente descripcion: object Buttonl: TButton Left = 152 Top = 1 0 4 Width = 75 Height = 25 Caption = ' B u t t o n l TabOrder = 0 end Ahora, si se modifica el nombre del objeto, su etiqueta o su posicion, por ejemplo, o se aiiade una nueva propiedad, estos cambios pueden copiarse y volver a pegarse en un formulario. Estos son algunos cambios de muestra: object B u t t o n l : TButton L e f t = 152 Top = 1 0 4 Width = 75 Height = 25 Capt ion = ' M i Bo ton ' TabOrder = 0 Font .Name = ' Arial' end Copiar esta descripcion y pegarla en el formulario creara un boton en la posi- cion especificada con la etiqueta Mi Boton con una fuente Arial. Para utilizar esta tecnica, es necesario saber como editar la representacion textual de un componente, que propiedades son validas para ese componente en particular y como escribir 10s valores para las propiedades de cadena, de conjunto y otras propiedades especiales. Cuando Delphi interpreta la descripcion textual de un componente o formula- rio, tambien puede cambiar 10s valores de otras propiedades relacionadas con aquellas que se han modificado y podria cambiar la posicion del componente, de forma que no solape con una copia previa. Por supuesto, si escribimos algo total- mente incorrect0 e intentamos pegarlo en un formulario, Delphi mostrara un men- saje de error indicando lo que ha fallado. Se pueden seleccionar diversos componentes y copiarlos a otro formulario o bien a1 editor de textos a1 mismo tiempo. Puede que esto resulte util cuando necesitamos trabajar con una serie de componentes similares. Podemos copiar uno en el editor, reproducirlo una serie de veces, realizar las modificaciones apro- piadas y, a continuacion, pegar todo el grupo de nuevo en el formulario. De las plantillas de componentes a 10s marcos Cuando copiamos uno o mas componentes de un formulario a otro, sencilla- mente copiamos todas sus propiedades. Una tecnica mas potente consiste en crear una plantilla de componentes, que hace una copia de las propiedades y del codigo fuente de 10s controladores de eventos. A1 pegar la plantilla en un nuevo formula- rio, seleccionando el pseudocomponente desde la paleta, Delphi reproduce el co- dig0 fuente de 10s controladores de eventos en el nuevo formulario. Para crear una plantilla de componentes, seleccionamos uno o m b componen- tes y activamos la orden del menu Component>Create Component Template. Esta abre el cuadro de dialog0 Component Template Information, en el que hay que introducir el nombre de la plantilla, la ficha de la Component Palette en la que deberia aparecer y un icono: De manera predeterminada, el nombre de la plantilla es el nombre del primer componente seleccionado, seguido de la palabra Template. El icono predefinido de la plantilla es tambien el icono del primer componente seleccionado, per0 se puede sustituir con un archivo de icono. El nombre que se de a la plantilla del componente sera el utilizado para describirlo en la Component Palette (cuando Delphi muestre la sugerencia contextual). Toda la informacion sobre las plantillas de componentes se almacena en un unico archivo, D E L P H I 3 2 . DCT, per0 aparentemente no hay forma de recuperar dicha informacion y editar una plantilla. Sin embargo, lo que si se puede hacer es colocar la plantilla de componentes en un formulario completamente nuevo, edi- tarlo e instalarlo de nuevo como una plantilla de componentes utilizando el mismo nombre. De este mod0 se podra sobrescribir la definicion anterior. TRUCO: Un grupo de programadores en Delphi puede compartir planti- llas de componentes si las guarda en un directorio comb, W i e n d o a1 Registro la entrada CCLibDir bajo la claw \SoftwareABorland\ Delphi\7.0\ComponentTemplates. La plantillas de componentes son muy comodas cuando hay distintos formula- rios que necesitan el mismo grupo de componentes y controladores de eventos asociados. El problema es que una vez que se ha colocado una instancia de la plantilla en el formulario, Delphi hace una copia de 10s componentes y de su codigo, que ya no se referira a la plantilla. No hay ninguna forma de modificar la definicion de la propia plantilla, ni tampoco es posible realizar el mismo cambio en todos 10s formularios que usan dicha plantilla. Pero si lo podemos hacer gra- cias a la tecnologia de marcos de Delphi. Un marco es una especie de panel con el que se puede trabajar en tiempo de diseiio de un mod0 similar a un formulario. Simplemente se crea un nuevo marco, se colocan algunos controles en el y se aiiade el codigo a 10s controladores de eventos. Cuando el marco estalisto, se abre un formulario, se selecciona el pseudocomponente Frame desde la ficha Standard de la Component Palette y se escoge uno de 10s marcos disponibles (del proyecto actual). Despues de colocar el marco en el formulario, lo veremos como si 10s componentes se hubieran copia- do en el. Si modificamos el marco original (en su propio diseiiador), las modifica- ciones apareceran reflejadas en cada una de las instancias del marco. Podemos ver un ejemplo sencillo, llamado Framesl, en la figura 1.8. Una imagen realmente no significa mucho, deberia abrir el programa o reconstruir uno similar si desea comenzar a utilizar frames. Framel T I1 IS Smt Smi (@dad Framel All I Figura 1.8. El ejemplo Framesl demuestra el uso de marcos El marco (a la izquierda) y su instancia en un formulario (a la derecha) permanecen en sincronia. A1 igual que 10s formularios, 10s marcos definen clases, por lo que encajan dentro del modelo orientado a objetos de la VCL con mayor facilidad que las plantillas de componentes. Como cabe imaginar a partir de esta introduccion breve, 10s marcos son una tecnica nueva realmente potente. Gestionar proyectos El Project Manager de Delphi (View>Project Manager) funciona con un grupo de proyectos, que puede englobar a su vez uno o mas proyectos. Por ejem- plo, un grupo de proyectos puede incluir una DLL y un archivo ejecutable o varios archivos ejecutables. Todos 10s paquetes abiertos apareceran como pro- yectos en la vista del Project Manager, incluso aunque no se hayan aiiadido a1 grupo de proyecto. En la figura 1.9, podemos ver el Project Manager con el grupo de proyecto del presente capitulo. Como se puede ver, el Project Manager se basa en una vista en arbol, que muestra la estructura jerarquica del grupo de proyectos, 10s proyectos y todos 10s formularios y unidades que lo componen. Podemos emplear la barra de herramientas y 10s menus de metodo abreviado mas complejos del Project Manager para trabajar con el. El menu de metodo abreviado funciona de acuerdo con el contexto: sus opciones dependen del elemento seleccionado. Hay elementos del menu para afiadir un nuevo proyecto o un proyecto esistente a1 grupo de proyecto, para compilar o crear un proyecto especifico o para abrir una unidad. Fata P&h - - - - - - - - - C Wchnos de programa\Borland\Delph17\Prolecls - ! 3 w e r n ~ me D \ r n ~ c o d e \ ~ ~ \ ~ ~ a g r a m ~ e r n o d 9 OtagrarnForrn D Lnd7caJe\Ol\D1agramDemo ttl a ToDoTesl exe D \md7code\0l\ToDoTest n J Fiamesl .ere D \rnd7caJe\Ol\Fiarnesl ,-' 3 Fum D hd7mde\Ol\Framesl 5 Fnm pas D \md7wdeWl\Frametl a Form1 D \md7codeWl\Fiamesl 13 @ Frame D \rnd7codeWl \Frames1 @ Flame pas D \md7code\Ol \Frames1 a Fianel D hd7mde\O1 \Flames1 Figura 1.9. El Project Manager de Delphi. I TRUCO: d i r d e Belphi 6; e f e e c t Manager muestra tambih 1bs pa- 1 qudes abiaitos, hdusoaunque no selfiayaediadido sl grupo de proyedos. Un paquete es'w ooleccion de componente Q & otras u n i d a h que se compila como un archim ejccuhble 3.p- M&o se vex&& a6elante. De todos 10s proyectos de un grupo, solo hay uno que esta activo y ese es el proyecto sobre el que trabajamos cuando seleccionamos una orden como Project>Compile. El menu desplegable Project posee dos ordenes para compi- lar o crear todos 10s proyectos del grupo. Cuando tengamos que crear diversos proyectos, podemos establecer un orden relativo usando las ordenes Build Sooner y Build Later. Estas dos ordenes basicamente reorganizan 10s pro- yectos de la lista. Entre las caracteristicas avanzadas del Project Manager, se encuentra la funcion de arrastre de archivos de codigo fuente desde carpetas de Windows o desde el Windows Explorer a un proyecto de la ventana del Project Manager para aiiadirlos a un proyecto (tambien se soporta este comportamiento para abrir archivos en el editor de codigo). Podemos ver facilmente que proyecto esta selec- cionado y cambiarlo utilizando el cuadro combinado de la parte superior de la ventana o utilizando la flecha hacia abajo que se encuentra junto a1 boton Run en la barra de herramientas de Delphi. Ademas de aiiadir archivos y proyectos de Pascal, se pueden aiiadir archivos de recurso de Windows a1 Project Manager; estos se compilan junto con el proyecto. Sencillamente, hay que desplazarse a un proyecto, seleccionar Add en el menu de metodo abreviado y escoger Resource File (*.rc) como tipo de archivo. Este archivo de recurso se unira automaticamente a1 proyecto, incluso aunque no haya una directiva $R correspondiente. Delphi guarda 10s grupos de proyectos con la extension .BPG (Borland Project Group). Esta caracteristica procede del C++ Builder y de 10s antiguos compiladores Borland C++, un historial que resulta claramente visible a1 abrir el codigo fuente de un grupo de proyectos, que basicamente corresponde a1 de un archivo makefile de un entorno de desarrollo C/C++. Veamos un ejemplo: # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MAKE = $ (ROOT) \bin\make.exe - $ (MAKEFLAGS) -f$** DCC = $ (ROOT) \bin\dcc32. exe $ * * BRCC = $ (ROOT) \bin\brcc32. exe $ * * # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PROJECTS = Project1 .exe Opciones de proyecto El Project Manager no ofrece una forma de definir las opciones para dos proyectos diferentes a la vez. Sin embargo, se puede recurrir a1 dialog0 Project Options desde el Project Manager en el caso de cada proyecto. La primera ficha de Project Options (Forms) muestra la lista de 10s formularios que se deberian crear automaticamente cuando arranca el programa y 10s formularios que crea el propio programa. La siguiente ficha (Application) se usa para esta- blecer el nombre de la aplicacion y el nombre de su archivo de ayuda y para escoger su icono. Otras posibilidades de Project Options estan relacionadas con el compilador y el editor de enlaces de Delphi, la informacion sobre la version y el uso de paquetes en tiempo de ejecucion. Existen dos formas de configurar las opciones del compilador. Una es utilizar la ficha Compiler del dialogo Project Options. La otra es definir o eliminar las opciones individuales del codigo fuente con las ordenes { $x+ } o { $x- } , en las que se reemplazaria la X por la opcion que queramos definir. Esta segunda tecni- ca resulta mas flexible, puesto que permite modificar una opcion solo para un archivo de codigo fuente concreto o incluso solarnente para unas cuantas lineas de codigo. Las opciones del nivel de fuente sobrescriben las opciones del nivel de compilacion. Todas las opciones de un proyecto se guardan automaticamente con el, per0 en un archivo a parte con una extension .DOF. Este es un archivo de texto que se puede editar facilmente. No se deberia eliminar dicho archivo si se ha modificado alguna de las opciones predefinidas. Delphi tambien guarda las opciones del compilador en otro formato, en un archivo CFG, para la compilacion desde la linea de comandos. Los dos archivos poseen un contenido similar per0 tienen un formato distinto: el compilador de la linea de comandos dcc no puede usar archi- vos .DOF, sino que necesita el formato .CFG. Tambien se pueden guardar las opciones del compilador pulsando Control-0- 0 (pulsar la tecla 0 dos veces mientras se mantiene pulsada la tecla Control). Esto inserta, en la parte superior de la unidad actual, directivas de compilador que corresponden a las opciones de proyecto en uso, como en el siguiente listado: I $ A + , B - , C+,D+, E - , F - , G + , H t , I t , J t , K - , L t , M - , N t , O+, P t , Q - , R - , S - , T - , U - , v t , W - , X + , Y t , Z l ) { $ M I N S T A C K S I Z E $ 0 0 0 0 4 0 0 0 ) {$MAYSTACKSIZE $ O O I O O O O O ) { $ I M A G E B A S E $ 0 0 4 0 0 0 0 0 ) { S A P P T Y P E G U I ) ISWARN SYMBOL-DEPRECATED O N ){$WARN SYMBOL-LIBRARY O N ) {$WARN SYMBOL-PLATFORM O N ) {$WARN U N I T - L I B R A R Y O N ) {$WARN UNIT-PLATFORM ON) {$WARN UNIT-DEPRECATED O N ) {$WARN HRESULT-COMPAT O N ) {$WARN HIDING-MEMBER O N ) {$WARN HIDDEN-VIRTUAL O N ) {$WARN GARBAGE O N ) {$WARN BOUNDS-ERROR O N ) {$WARN ZERO-NIL-COMPAT O N ) {$WARN STRING-CONST- TRUNCED O N ) {$WARN FOR-LOOP-VAR-VARPAR O N ) {$WARN TYPED-CONS T-VARPAR O N ) {$WARN ASG- TO- TYPED-CONST O N ) {$WARN CASE-LABEL-RANGE O N ) {$WARN FOR-VARIABLE O N ) {$WARN CONS TRUCTING-ABS TRACT ON) {$WARN COMPARISON-FALSE ON) {$WARN COMPARISON- TRUE ON) {$WARN COMPARING- S IGNED- UNSIGNED ON) $ WARN COMBINING- S I G N E D UNSIGNED ON) {$WARN UNSUPPORTED-CONS TRUCT ON) {$WARN FILE-OPEN ON) {$WARN FILE-OPEN-UNITSRC ON) {$WARN BAD-GLOBAL-SYWBOL ON) {$WARN DUPLICATE-CTOR-DTOR ON) {$WARN I N V A L I D - D I R E C T I V E ON) {$WARN PACKAGE-NO- L I N K ON) {$WARN PACKAGED- THREADVAR ON) {$WARN I M P L I C I T - IMPORT ON) {$WARN HPPEMI T- IGNORED ON) {$WARN NO-RETVAL ON) {$WARN USE-BEFORE-DEF ON) {$WARN FOR-LOOP-VAR-UNDEF ON) {$WARN UNIT--MISMATCH ON) {$WARN NO-CFG-FILE-FOUND ON) {$WARN MESSAGE-DIRECTIVE ON) {$WARN I M P L I C I T - V A R I A N T S ON) {$WARN UNICODE- TO-LOCALE ON) {$WARN LOCALE- TO- UNICODE ON) {$WARN IMAGEBASE-MULTIPLE ON) {$WARN SUSPICIOUS-TYPECAST ON) {$WARN PRIVATE-PROPACCESSOR ON) {$WARN UNSAFE- TYPE O F F ) {$WARN UNSAFE-CODE OFF) ISWARN UNSAFE-CAST OFF) Compilar y crear proyectos Existen diversas formas de compilar un prbyecto. Si se ejecuta (pulsando F9 o haciendo clic sobre el icono Run de la barra de herramientas), Delphi lo compila- ra primero. Cuando Delphi compila un proyecto, compila unicamente 10s archi- vos que han cambiado. En cambio, si seleccionamos Compile>Build>All, se compilan todos 10s archivos, aunque no hayan cambiado. Esta segunda orden deberia ser necesaria en raras ocasiones, puesto que Delphi puede decidir normal- mente que archivos han cambiado y compilarlos como sea necesario. La unica excepcion son 10s casos en 10s que se cambian algunas opciones de proyecto, en cuyo caso debemos emplear la orden B u i l d A l l para que las nuevas opciones Sean efectivas. Para crear un proyecto, Delphi compila primero cada archivo de codigo fuente y crea una unidad compilada de Delphi (DCU). (Este paso se realiza solo si el archivo DCU no se ha actualizado aun). El segundo paso, que realiza el editor de enlaces, consiste en mezclar todos 10s archivos DCU en un archivo ejecutable, opcionalmente con codigo compilado de la biblioteca VCL (si no hemos decidido utilizar paquetes en tiempo de ejecucion). El tercer paso consiste en unir a un archivo ejecutable cualquier archivo de recurso opcional, como el archivo RES del proyecto, que contiene su icono principal y 10s archivos DFM de 10s formula- rios. Se pueden entender mejor 10s pasos de la compilacion y seguir el hilo de lo que ocurre durante dicha operacion si activamos la opcion Show Compiler Progress (en la pagina Preferences del cuadro de dialog0 Environment Options). - - ADVERTENCIA: Del@ no siempre tiene claro cuhijo feconstruir las unidadcs basadas en ah.bs unidades que se han m o d i f i d . Esto s s sobre todo verdad en 10s casos (y son muchos) en que la i n t e r v W 4 n -deI usuario confunde la logica del wmpilador. Por ejemplo, r m m b n u adiivas, modi- #3catAcodigo.fuente desde el exterior del IDE. copiar archiui,s tk &.&go heate ~n&uos Q wchivos DCU a1 disco, o tmer multiples ~ o p i a s de un archiyo fuentc dc'oni~ en la ruta dc busqucda puede e s t r o p k el proceso dc compiIaclln. Ctqh vez que el compilador muestra &6n naensaje de error estraiio, 1~ primer0 que deberiamos hacer es utilizarla oFden Bui ld XLl pard sincrohizar de nuevo l a caracteristica make (dq @mdmcci6n) corilos uchivas acfuafes del disco. . - La orden Compile se puede usar solo cuando se ha cargado un proyccto en el cditor. Si no hay ningun proyecto activo y cargamos un archivo fuente Pascal, no se puede compilar. Sin embargo, si cargamos el archivo fuente como si fuera un proyecto, podremos compilar el archivo. Para ello, simplemente seleccionamos el boton de la barra de herramientas Open Project y cargamos un archivo PAS. Ahora podemos verificar su sintaxis o compilarlo, creando un DCU. Ya mencionamos que Delphi permite el uso de paquetes en tiempo de ejecu- cion, lo que afecta a la distribucion del programa mas que a1 proceso de compila- cion. Los paquetes Delphi son bibliotecas de cnlace dinamico (DLL) que contienen componentes Delphi. A1 emplear paquetes, se consigue que un archivo ejecutable sea mucho mas pequeiio. Sin embargo, el programa no se ejecutara a no ser que este disponible la biblioteca dinamica apropiada (corno vcl50. bpl, que es bastante amplia) en el ordenador en el que desea ejecutar el programa. Si sumamos el tamafio de esta biblioteca dinamica a1 del pequeiio archivo ejecutable, la cantidad total de espacio en disco necesaria por el aparentemente pcqueiio programa, que hemos creado con 10s paquetes en tiempo de ejecucion, es mucho mayor que el espacio necesario por el supuestamente gran archivo ejecuta- ble por si solo. Por supuesto, si tenemos diversas aplicaciones en un unico siste- ma, ahorraremos mucho, tanto en espacio de disco como en consumo de memoria en tiempo de ejecucion. El uso de paquetes suele ser recomendable, pero no siem- pre. En ambos casos, 10s ejecutables Delphi resultan extremadamente rapidos de compilar y la velocidad de la aplicacion que obtenemos es comparable a la de un programa en C o C++. El codigo compilado en Delphi se ejecuta al menos cinco vcccs mas rapido que el codigo equivalente de herramientas interpretadas o "scmicompiladas". Ayudante para mensajes del compilador y advertencias Ademas de 10s clasicos mensajes del compilador, Delphi 7 ofrece una nueva ventana con informacion adicional sobre algunos mensajes de error. Esta ventana se activa mediante el comando de menu View>Additional Message Info. Mues- tra informacion almacenada en un archivo local, que puede actualizarse descar- gando una version nueva desde el sitio Web de Borland. Otro cambio en Delphi 7 esta rclacionado con el mayor control que se tiene sobre las advertencias del compilador. El cuado de dialogo Project Options incluye ahora una pagina Compiler Message en la que se pueden seleccionar muchas advertencias individuales. Esta posibilidad se aiiadio probablemente por el hecho de que Delphi 7 tiene un nuevo conjunto de advertencias relacionadas con la compatibilidad con la futura herra- mienta Delphi for .NET. Estas advertencias son bastante exhaustivas, y se pueden inhabilitar como se muestra en la figura 1.10. I yarn- - - - - - - - -. 3 Unit idenlifier does m t match hle name ?. % Na sonl~gu~at~an files laund - ,JJ User message B lmpl~ut use of Varlants unit Errol conveltrng Unicode cha~ to locale charsel @ Er~or converl~q locals s l i i ~ to Unicode ,4 Imagebase e not a rmkiile d 64k M 1 LI'.Unsafe typecast i 1 - Figura 1.10. La nueva pagina Compiler Messages del cuadro de dialogo Project Options. TambiCn se pueden habilitar o inhabilitar algunas de estas advertencias me- diante opciones de compilador como estas: ( $Warn UNSAFE-CODE OFF) { $Warn UNSAFE-CAST OFF) ($Warn UNSAFE-TYPE OFF) En general, es mejor mantener estas opciones fuera del codigo fuente del pro- grams, algo que finalmente permite Delphi 7. Exploracion de las clases de un proyecto Delphi siempre ha incluido una herramienta para explorar 10s simbolos de un programa compilado, aunque el nombre de csta herramienta haya carnbiado mu- chas veces (desde Object Browser a Project Explorer y ahora a Project Browser). En Delphi 7, se puede activar la ventana del Project Browser me- diante la opcion de menu View>Browser, que muestra la misma ventana que la figura1 . 1 1 . El explorador permite analizar la estructura jerarquica de las clases de un proyccto y buscar en ella 10s simbolos y lineas del codigo fuente en que se haga referencia a ellos. Figura 1.11. Project Browser Al contrario que el Code Explorer, el Project Browser so10 se actualiza cuando se vuelve a compilar el proyecto. Este explorador permite ver listas de clases, unidades y globales, y tambien escoger si buscar so10 simbolos definidos en el proyecto o tanto del proyecto como de la VCL. Se puede cambiar la configu- ration del Project Browser y la del Code Explorer en la pagina Explorer de Environment Options o mediante el comando P r o p e r t i e s del menu desple- gable del Project Browser. Algunas de las categorias que se pueden ver en esta ventana son especificas del Project Browser; otras estan relacionadas con am- bas herramientas. Herramientas Delphi adicionales y externas Ademas del IDE, a1 instalar Delphi se consiguen otras herramientas externas. Algunas de ellas, como el Database Desktop, el Package Collection Editor (PCE . e x e ) y el Image Editor (1magEdi t . e x e ) estan disponibles desde el menu Took del IDE. Ademas, la edicion Enterprise posee un enlace a1 SQL Monitor (S qlMon . exe ) . Otras herramientas a las que no se puede acceder di- rectamente desde el IDE son, por ejemplo, las herramientas de linea de comandos que se pueden encontrar en el directorio bin de Delphi. Por ejemplo, entre estas herramientas se incluye el compilador de Delphi en linea de comandos (DCC3 2 . e x e ) , un compilador de recursos de Borland (BRC3 2 . e x e y BRCC3 2 . e x e ) y un visor de ejecutables (TDump . exe) . Por ultimo, algunos de 10s programas de ejemplo que se incluyen con Delphi son en realidad utiles herramientas que el usuario puede compilar y tener siempre a mano. Aqui se presentan algunas de ellas, las de mas alto nivel, la mayoria disponibles en la carpeta \ D e l p h i 7 \ b i n y en el menu Tools: Web App Debugger (WebAppDbg . exe ) : Es el servidor de depuracion Web introducido en Delphi 6. Se utiliza para guardar la pista de segui- miento de las solicitudes que el usuario envia a sus aplicaciones y para depurarlas. Este depurador se rescribio en Delphi 7: ahora se trata de una aplicacion CLX y su conectividad se basa en sockets. XML Mapper (XmlMapper . exe ) : Es una herramienta para crear trans- formaciones XML aplicadas a1 formato producido por el componente ClientDataSet. External Translation Manager (e tm60. exe) : Es la version indepen- diente del Integrated Translation Manager. Esta herramienta externa pue- de ofrecerse a traductores externos y esta disponible desde Delphi 6. Borland Registry Cleanup Utility (D7RegClean. exe ) : Ayuda a eli- minar todas las entradas de Registro aiiadidas por Delphi 7 a un ordena- dor. TeamSource: Es un sistema de control de versiones avanzado proporcio- nado con Delphi, que comenzo con la version 5. La herramienta es muy similar a su antigua encarnacion y se instala separadamente de Delphi. Delphi 7 ofrece la version 1.0 1 de TeamSource, la misma version disponi- ble despues de aplicar un parche disponible para la version de Delphi 6. WinSight (Ws32 . exe ) : Es un programa Windows "espia de mensajes" disponible en el directorio b i n . Database Explorer: Puede activarse desde el IDE de Delphi o como herra- mienta independiente, usando el programa ~ B E x p l o r . e x e del directo- rio b i n . Ya que esta destinada a BDE, no se utiliza mucho actual- mente. OpenHelp (oh. exe) : Es la herramienta que podemos emplear para admi- nistrar la estructura de 10s propios archivos de ayuda de Delphi, integran- do archivos de otras personas en el sistema de ayuda. Convert ( C o n v e r t . e xe): Es una herramienta de linea de comandos que podemos usar para convertir 10s archivos DFM en su descripcion textual equivalente y viceversa. Turbo Grep (Grep . exe) : Es una utilidad de busqueda de lineas de orde- nes, mucho mas rapida que el arraigado mecanismo Find In Files, per0 no es facil de usar. Turbo Register Server (TRegSvr . exe ) : Es una herramienta que pode- mos emplear para registrar bibliotecas ActiveX y servidores COM. El co- dig0 fuente de esta herramienta esta disponible bajo \Demos \ A c t i veX\ TRegSvr . Resource Explorer: Es un poderoso visor de recursos (pero no un editor de recursos propiamente dicho) que se puede encontrar bajo \Demos \ R e s X p l o r . Resource Workshop: Es un editor de recursos de 16 bits que puede con- trolar archivos de recursos de Win32. El CD de instalacion de Delphi incluye una instalacion independiente para esta herramienta. Se ofrecia con 10s compiladores de Borland para C++ y Pascal para Windows y era mucho mejor que 10s editores de recursos de Microsoft disponibles enton- ces. Aunque su interfaz de usuario no se ha actualizado y no trabaja con nombres de archivos largos, esta herramienta todavia resulta muy util para construir recursos especiales o personalizados. Tambien le permite explo- rar 10s recursos de 10s archivos ejecutables existentes. Los archivos creados por el sistema Delphi produce diversos archivos para cada proyecto y seria conveniente sa- ber que son y como se denominan. Basicamente, hay dos elementos que influyen en la forma de denominacion de 10s archivos: 10s nombres que se dan a un proyec- to y a sus unidades, y las extensiones predefinidas de 10s archivos que utiliza Delphi. En la tabla 1.1 se listan las extensiones de 10s archivos que encontrara en el directorio en el que se guarda un proyecto Delphi. La tabla muestra tambien cuando o en que circunstancias se crean estos archivos y su importancia de cara a su posterior cornpilacion. Tabla 1.1. Extensiones de archivos de proyecto Delphi BMP, CUR BPG BPL CAB .CFG .DCP ICO, Archivos de mapas de bits, iconos y cursores: archivos estandar de Windows usa- dos para almacenar ima- genes de mapas. Borland Project Group (Grupo de proyectos Borland): archivos que usa el nuevo Project Manager. Es una especie de makefile. Borland Package Library (Biblioteca de paquetes Borland): una DLL que contiene, entre otros, com- ponentes VCL que usa el entorno Delphi en tiempo de disefio o las aplicacio- nes en tiempo de ejecu- cion. (Estos archivos usaban una extension .DPL en Delphi 3.) Formato de archivo com- primido Microsoft Cabinet usado por Delphi para el despliegue Web. Un archi- vo CAB puede contener diversos archivos compri- midos. Archivo de configuracion con las opciones de pro- yecto. Similar a 10s archi- vos DOF. Delphi Component Packa- ge (Paquete de componen- tes de Delphi): un archivo con informacion de simbo- Desarrollo: Image Editor Desarrollo Compilacion: Enlace Compilacion Desarrollo Compilacion Normalmente no, per0 pueden ser necesarios en tiempo de ejecu- cion y para una poste- rior modificacion. Necesario para compi- lar de nuevo todos 10s proyectos del grupo a la vez. Se distribuiran a otros desarrolladores Delphi y opcionalmente a usuarios finales. Distribuido a usuarios Necesario han definido opciones especiales de compi- lacion. Necesario cuando usamos paquetes. Solo se distribuira a otros desarrolladores .DCU . DDP .DFM .-DF lo para el codigo compilado en el paquete. No incluye codigo compilador, que se guarda en archivos DCU. Delphi Compiled Unit (U ni- dad compilada Delphi): re- sultado de la compilacion de un archivo en Pascal. El n uevo Delphi Diagram Portfolio (Cartera de diagrama Delphi) usado por la vista en diagrarna del editor (era DTI en Delphi 5 ) . Delphi Form File (Archivo de formulario de Delphi): un archivo binario con la descripcion de las propie- dades de un formulario (o un modulo de datos) y de 10s componentes que con- tiene. Copia de seguridad de Delphi Form File (DFM) Com pilacion Desarrollo Desarrc Desarrollojunto con 10s archivos BDPL. Se puede compilar una aplica- cion con las unidades de un paquete simple- mente con el archivo DCP y el BPL (sin archivos DCU). Solo si el codigo fuente no esta dispo- nible. Los archivos DCU de las unidades que escribimos son un paso intermedio, por lo que favorecen una compilacion mas rapida. No. Este archivo almacena informacion "solo en tiempo de disefio" no necesaria para el programa resultante per0 muy importante para el programador. Si. Todos 10s formula- rios se almacenan tanto en un archivo PAS como en un DFM. No. Este archivo se crea al guardar una version de la unidad relacionada con el formulario y el archivo del formulario junto con ella. DFN Archivo de soporte para Desarrollo (ITE) Si (para el ITE). Estos Integrated Translation Environment (hay un archi- vo DFN para cada formula- rio y cada lenguaje o bjetivo). DLL Dinamic Link Library (Bi- Cornpilacion blioteca de enlace dinami- Enlace co): otra version de un archivo ejecutable. DOF Delphi Option File: archivo Desarrollo de texto con la configura- cion actual de las opciones actuales para las opciones del proyecto. DPK y ~ h o r a tam- lien .DPKW r .DPKL DPR -DP DSK Delphi Package: el archivo Desarrollo de codigo fuente del pro- yecto de un paquete (o un archivo de proyecto espe- cifico para Windows o Linux). Archivo Delphi Project. Desarrollo (Este archivo contiene en realidad codigo fuente Pascal.) Copia de seguridad del ar- Desarrollo chivo Delphi Project (.DPR). Desktop file (Archivo de es- Desarrollo critorio): contiene infor- macion sobre la posicion de las ventanas Delphi, 10s archivos que se abren en el editor y otras configura- ciones del escritorio. archivos contienen cadenas traducidas para cada formulario que editarnos en el Translation Manager Vease .EXE Necesario solo si se han instalado opcio- nes especiales del compilador. Si. No. Este archivo se crea automaticarnente al guardar una nueva version de un archivo de proyecto. No. En realidad debe- rian eliminarse si se copia el proyecto en un nuevo directorio. DSM EXE HTM LIC OBJ . OCX .PAS Delphi Symbol Module (Mo- Cornpilacion dulo de simbolos Delphi): (pero solo si se Alrnacena toda la informa- ha activado la cion de sirnbolo del explora- opcion Save dor. Symbols) Executable file (Archivo eje- Compilacion: cutable): la aplicacion Enlace Windows creada. 0 .HTML (Hypertext Despliegue Web Markup Language, Len- de un guaje de rnarcas con ActiveForrn hipertexto): el forrnato de archivo usado para pagi- nas Web. Los archivos de licencia Asistente relacionados con un archi- ActiveX y otras vo OCX. herrarnientas Object file (Archivo objeto) Paso intermedio (cornpilado), tipico del de compilacion, rnundo C/C++. generalmente no se usa en Delphi. OLE Control Extension (Ex- Compilacion: tension de control OLE): Enlace una version especial de una DLL, que contiene controles ActiveX o forrnularios. Pascal file (Archivo de Desarrollo Pascal): El codigo fuente de una unidad Pascal, una unidad relacionada con un forrnulario o una unidad independiente. No. El Object Browser usa este archivo, en lugar de 10s datos en memoria, cuando no puede volver a compi- lar un proyecto. No. Este archivo que se distribuira incluye todas las unidades compiladas, forrnula- rios y recursos. No. No participa en la cornpilacion del pro- yecto. No. Es necesario usar el control en otro entorno de desarrollo. Podria ser necesario para rnezclar Delphi con codigo cornpilado C++ en un tinico proyecto. Vease .EXE. -PA RES, .RC .RPS .TLB TODO .UDL Copia de seguridad de un Desarrollo archivo Pascal (.PAS). No. Este archivo lo crea Delphi autorna- ticamente al guardar una nueva version del codigo fuente. Resource file (Archivo de recurso): el archivo binario asociado con el proyecto de una aplicacion y que normalmente contiene su icono. Podernos afiadir otros archivos de este tip0 a un proyecto. Cuando creamos archivos de recur- so personalizados pode- rnos usar tambien el forrnato textual .RC. Cuadro de dialo- go Development Options. El ITE (Integrated Translation Environment) crea archivos de recurso con comentarios especiales. Translation Repository Desarrollo (ITE) (parte de Integrated Translation Environment). Type Library (Biblioteca de Desarrollo tipos): un archivo creado de forrna autornatica o por el Type Library Editor para aplicaciones del servidor OLE. Archivo de lista To-do en Desarrollo el que se guardan elernen- tos relacionados con el proyecto entero. Microsoft Data Link (Enlace Desarrollo de datos Microsoft). Si. Delphi crea de nuevo el archivo RES principal de una apli- cacion en funcion de la inforrnacion de la ficha Application del cuadro de dialogo Project Options. No. Necesario para adrninistracion de las traducciones. Este es un archivo que pueden necesitar otros prograrnas OLE. No. Este archivo contiene notas para 10s programadores. Usado por ADO para referirse a un provee- dor de datos. Similar a un alias en el entor- no BDE. Ademas de 10s archivos creados durante el desarrollo de un proyecto en Delphi, existen muchos otros creados y usados por el propio IDE. En la tabla 1.2, se rn presenta una breve lista de las extensiones que merece la pena conocer. La mayo- ria de estos archivos estan en formatos propietarios no documentados, por lo que poco se puede hacer con ellos. Tabla 1.2. Extensiones de archivo de personalizacion del IDE de Delphi seleccionadas. . DC I Delphi Code Templates (Plantillas de codigo Delphi). .DRO Delphi Object Repository (Object Repository de Delphi) (De- beria modificarse el Repository con la orden Tools> Repository.) . DMT Delphi Menu Templates (Plantillas de menu de Delphi). .DBI Database Explorer Information (Informacion del explorador de bases de datos). .DEM Delphi Edit Mask (Mascara de edicion de Delphi) (Archivos con formatos especificos seglin paises para mascaras de edicion). . DCT Delphi Component Template (Plantillas de componentes de Delphi). .DST Desktop Settings File (Archivo de configuracion del escrito- I rio): uno para cada configuracion de escritorio definida. Un vistazo a 10s archivos de codigo fuente Los archivos basicos de Delphi son 10s archivos de codigo fuente en Pascal, que son archivos de testo ASCII normales. El texto en negrita, cursiva y colorea- do que se ve en el editor depende de la forma en que se resalte la sintaxis, per0 no se guarda esa configuracion junto con el archivo. Merece la pena destacar que hay un unico archivo para todo el codigo del formulario, no solo pequeiios frag- mentos de codigo. - . . - - - - - - - - . - - - - - ----- TRUCO: En 10s listados de este libro, hemos asociado la fnrma de regaltar la sintaxis en negrita del editor a las palabras clave y la cnrsira p lrts cadenas y 10s comentarios. En el caso de un formulario, el archivo Pascal contiene la declaracion de clase del formulario y el codigo fuente de 10s controladores de eventos. Los valores de las propiedades que se definen en el Object Inspector se almacenan en un archi- vo a parte con la descripcion de formulario (con extension .DFM). La unica excepcion es la propiedad Name, que se usa en la declaracion de formulario para hacer referencia a 10s componentes del formulario. El archivo DFM es de manera predeterminada una representacion textual del formulario, pero se puede guardar en cl formato binario tradicional Resource de Windows. Podemos establecer el formato predefinido que queremos usar para proyectos nuevos en la ficha Preferences del cuadro de dialog0 Environment Options y cambiar el formato de formularios individuales con la orden Tcxt DFM del menu de metodo abreviado de un formulario. Un editor de texto normal puede leer solo la version de texto.Sin embargo, se pueden cargar 10s archivos DFM de ambos tipos en el editor Delphi, que 10s convertira primero, si cs neccsa- rioj en una descripcion textual. La forma mas sencilla de abrir la descripcion textual dc un formulario (sea en el formato que sea) es seleccionar la orden View A s Text del menu de metodo abreviado del Form Designer. Esta orden cierra el formulario, lo guarda si es necesario y abre el archivo DFM en el editor. Mas tarde sc puede volver a1 formulario usando la orden View A s Form del menu dc metodo abreviado de la ventana del editor. En realidad, se puede editar la descripcion textual de un formulario, aunquc esto deberia hacerse con extremo cuidado. Desde el momento en que se guarde el archivo, se convertira de nuevo en un archivo binario. Si se han hecho cambios incorrectos, se detendra la compilacion con un mensaje de error y habra que corregir el contenido del archivo DFM antes de volver a abrir el formulario. Por esa razonj no se deberia intentar cambiar la descripcion textual de un formulario manualmentc hasta disponcr de un solido conocimiento de programacion en Delphi. propiedades m k relevantes, por lo general, he elirninado las propiedades de posicion, 10s valores binarios y otra lineas que ofiecen poca informacion. - TRUCO: En el libro, aparecen normalmente extractos de archivos DFM. En l a mayoridde estos extractos, aparecen unicamente 10s componentes o 1 Ademas de 10s dos archivos que describen el formulario (PAS y DFM), hay un tercer archivo que resulta vital para volver a construir la aplicacion. Este es el archivo de proyecto de Delphi (DPR), otro archivo de codigo f~iente en Pascal, que se crea automaticamente y que rara vez es necesario modificar manualmente. Puede verlo con la orden del menu View>Project Source. Algunos de 10s demas archivos menos relevantes creados por el IDE usan la estructura de archivos IN1 de Windows, en la que cada seccion se indica mediante un nombre que va entre corchetes. Por ejemplo, este es un fragment0 de un archi- vo de opciones (DOF). [Compiler] A= 1 B=O [Linker] MinStackSize=16384 MaxStackSize=1048576 ImageBase=4194304 [Parameters] Runparams= HostApplication= Los archivos de escritorio (DSK) utilizan la misma estructura, que contiene el estado del IDE de Delphi para un proyecto especifico, listando la posicion de cada ventana. [Mainwindow] Create=l Visible=l State=O Lef t=2 Top=O Width=800 Height=97 El Object Repository Delphi tiene varias ordenes de menu que se pueden usar para crear un nuevo formulario, una nueva aplicacion, un nuevo modulo de datos, un nuevo compo- nente, etc. Dichas ordenes estan situadas en el menu File>New y en otros menus desplegables. Si seleccionamos sencillamente File>New>Other, Delphi abre el Object Repository, que se usa para crear nuevos elementos de cualquier tipo: formularios, aplicaciones, modulos de datos, objetos thread, bibliotecas, compo- nentes, objetos de automatizacion y muchos mas. El nuevo cuadro de dialogo (que se ve en la figura 1.12) posee varias fichas, contiene todos 10s elementos que puede crear, 10s formularios existentes y 10s proyectos almacenados en el Repository, asistentes Delphi y 10s formularios del proyecto actual. Las fichas y las entradas de este cuadro de dialogo con solapas dependen de la version especifica de Delphi, por lo que no las mencionaremos. por techa o por descnpcl6n) y mostrat dttim%&s ViMw (idol3 giqhks, iconos pequefios, listas y detalles). La vista Msib propo*ei& & hes- cripcion, autor y fecha de la herramienta, una informacion que resulta so- bre todo importante cuando se echa un vistazo a los asistentes, proyectos o formularios que hemos aiiadido a1 Repository. Cunlrol Panel Appbcalmn TI Flame 1 - C Control Panel Dda Module Module Package Ploiect GI- Console Cwone* Applcat~on DCC W~zad Fam Resource DLL Service Wnald Figura 1.12. La primera pagina del cuadro de dialogo New Items, generalmente corno Object Repository. conocida El mod0 mas sencillo de personalizar el Object Repository es aiiadir nuevos proyectos, formularios y modulos de datos como plantillas. Tambien podemos aiiadir fichas nuevas y organizar 10s elementos de algunos de ellas (sin incluir las fichas New ni la del proyecto en uso). Cuando se tiene una aplicacion en funcio- namiento que se quiere emplear como punto de arranque para el desarrollo de programas similares, se puede guardar el estado actual en una plantilla para usarla mas tarde. Simplemente se usa la orden Project>Add To Repository y se , cubre su cuadro de dialogo. Tambien se pueden aiiadir nuwas plantillas de formulario. Sencillamente des- plazamos el formulario que se quiere aiiadir y seleccionamos la orden ~ d d T o R e p o s i t o r y del menu de metodo abreviado. A continuacion, indicamos el titu- lo, la descripcion, el autor, la ficha y el icono en su cuadro de dialogo. Hay que tener en cuenta que si se copia un proyecto o una plantilla de formulario a1 Repository y se vuelve a copiar a otro directorio, simplemente se realiza una operacion de copiar y pegar; no es muy distinto de copiar 10s archivos manual- mente. I La plmWla de pmyecb en blanco I Cuando se inicia un nuevo proyecto, autodticamente se abre tambib un formulario en blgaco. Sin embargo, si queremos basar el nuevo proyecto en urn, de 10s objetos de fbmularios o asistentes, habra que afiadir una planti- hay que seguir para ello son: 1. Crear un nuevo proyecto como de costumbre. 2. Eliminar el unico formulario del proyecto. 3. Aiiadir este proyecto a las plantillas y denominado Empty Project. Cuando seleccionamos este proyecto en el Object Repository, se obtienen dos ventajas: tener su proyecto sin un formulario y poder escoger un direc- torio en el que se copiaran 10s archivos de la plantilla de proyecto. Tambien hay una desventaja, habra que recordar usar la orden FileSave Project AS para dar un nuevo nombre a1 proyecto, porque si se guarda el proyecto de otro modo se utiliza automaticamente el nombre predefinido de la plan- tilla. Para personalizar aun mas el Repository se puede usar la orden Tools> Repository. Esta orden abre el cuadro de dialog0 del Object Repository, que se puede emplear para mover elementos a distintas fichas, aiiadir elementos nuevos o borrar 10s que ya existen. Incluso se pueden aiiadir fichas nuevas, darles un nombre nuevo o eliminarlas y cambiar su orden. Un elemento importantc de la instalacion del Object Repository es el uso de 10s valores predefinidos: Usar la casilla de verificacion New Form bajo la lista dc objetos para designar un formulario como el que se usara cuando se Cree un nuevo formulario (File>New Form). La casilla de verificacion del Main Form indica que tipo de formulario emplear cuando se crea el formulario principal de una nueva aplicacion (File>New Application), si no se selecciona un New Project especial. La casilla de verificacion New Project, disponible cuando se selecciona un proyecto, marca el proyecto por defecto que Delphi utilizara cuando se de la orden File>New Application. Solo un formulario y un proyecto del Object Repository pueden tener cada una de estas tres configuraciones marcadas con un simbolo especial situado sobre su icono. Si no se selecciona ningun proyecto como New Project, Delphi crea un proyecto por defecto basado en el formulario marcado como Main Form. Si no hay ningun formulario marcado como Main Form, Delphi crea un proyecto por defecto con un formulario en blanco. Cuando trabajamos con el Object Repository, trabajamos con formularios y modulos guardados en el subdirectorio OBJREPOS del directorio principal de Delphi. En este momento, si usamos un formulario u otro objeto directamente sin copiarlo, acabaremos teniendo algunos archivos de nuestro proyecto en este directorio. Es importante darse cuenta de como funciona cl Repository, porque si queremos modificar un proyecto o un objetoguardados en 61; la mejor tecnica es trabajar con 10s archivos originalcs, sin copiar 10s datos una y otra vez en el Repository. . lnstalar nuevos asistentes DLL Tecnicamente, 10s nuevos asistentes poseen dos formas diferentes: pueden formar parte de 10s componentes o de 10s paquetes o pueden distribuirse como archivos DLL independientes. En el primer caso, se instalaria del mismo mod0 que un componente o un paquete. Cuando se recibe una DLL independiente, hay que aiiadir el nombre de la DLL a1 Registro de Windows baio la clave \Sof tware\Borland\DelDhi\7.O\Ex~erts. Sirn- a . . plemente se aiiade una nueva clave de cadena bajo esta clave, se escoge un nombre (no importa realmente cual) y se utiliza como texto la ruta y nom- L - _ J - ---l-_.- 3 - 1 __:_*_-A_ n m T 0 . . 2 . _ - I - _ _..*---l-_ -..- _._ _ _ L Z _ _ ore a e arctuvo ael asmenre ULL. 3 e pueaen ver las enrraaas que ya estan presentes bajo la clave Experts para averiguar el mod0 en que se debe introducir la ruta. Actualizaciones del depurador en Delphi 7 Cuando se ejecuta un programa en el IDE de Delphi, generalmente se arranca en el depurador integrado. Se pueden fijar puntos de ruptura, ejecutar el codigo linea a linea y explorar sus detalles internos, como el codigo ensamblador que se ejecuta y el uso de 10s registros de la CPU en la vista CPU. Para mencionar un par de las nuevas caracteristicas del depurador, en primer lugar el cuadro de dialog0 Run Parameters en Delphi 7 permite establecer un directorio de traba.jo para el programa que se va a depurar. Esto significa que el directorio actual sera el que se indique, no aquel en el que se haya compilado del programa. Otra modificacion importante tiene que ver con la Watch List. Ahora dispone de multiples pestaiias que permiten mantener un conjunto distinto de escuchas de variables activo para las distintas areas del programa que se esta depurando, sin amontonarse en una unica ventana. Puede aiiadirse un grupo nue- vo a la Watch List mediante su menu abreviado y tambien modificar la visibilidad de las cabeceras de las columnas y habilitar escuchas individuales con sus corres- pondientes casillas de activacion. -- Wc_hName ' V W - - _ - _ . . . - rn Controls ' ' expected but end of lde found rn TRad~oButton Symbol was el~mtnated by l~nker I El lenguaje de programaclon Delphi El entorno de desarrollo para Delphi se basa en una extension orientada a objetos del lenguaje de programacion Pascal conocida como Object Pascal o Pascal orientado a objetos. Recientemente, Borland declaro su intencion de referirse a1 lenguaje como "el lenguaje Delphi", probablemente porque la empresa deseaba ser capaz de decir que Kylix usa el lenguaje Delphi y porque Borland ofrecera el lenguaje Delphi sobre la plataforma .NET de Microsoft. Debido a la costumbre de 10s aiios, es comun utilizar ambos nombres por igual. La mayoria de 10s lenguajes de programacion modernos soportan programa- cion orientada a objetos (OOP). Los lenguajes OOP se basan en tres conceptos fundamentales: la encapsulacion (normalmente implementada mediante clases), la herencia y el polimorfismo (o enlace tardio). Aunque se puede escribir codigo Delphi sin comprender las caracteristicas principales del lenguaje, no es posible dominar este entorno hasta que se comprende totalmente el lenguaje de programa- cion. Este capitulo trata 10s siguientes temas: Clases y objetos. Encapsulacion: p r i v a t e y pub1 ic. Uso de propiedades. Constructores. Objetos y memoria. Herencia. Metodos virtuales y polimorfismo. Conversion de tipos segura (informacion de tip0 en tiempo de ejecucion). Interfaces. Trabajo con excepciones. Referencias de clase. Caracteristicas centrales del lenguaje El lenguaje Delphi es una extension OOP del clasico lenguaje Pascal, que Borland ha liderado durante muchos aiios con sus compiladores Turbo Pascal. La sintaxis del lenguaje Pascal suele considerarse bastante explicita y mas legible que, por ejemplo, el lenguaje C. Su extension orientada a objetos sigue el mismo enfoque, ofreciendo la misma potencia de 10s recientes lenguajes OOP, desde Java a C#. Incluso el nucleo del lenguaje esta sujeto a cambios continuos, per0 algunos de ellos afectaran a las necesidades diarias de programacion. En Delphi 6, por ejem- plo, Borland aiiadio el soporte para varias caracteristicas mas o menos relaciona- das con el desarrollo de Kylix, la version para Linux de Delphi: Una directiva nueva para la compilacion condicional ($IF). Un conjunto de directivas de sugerencia ( p l a t f o r m , d e p r e c a t e y l i b r a r y , de las cuales solo se suele usar la primera) y la nueva directiva $WARN que se utiliza para inhabilitarlas. Una directiva $MESSAGE para emitir informacion personalizada entre 10s mensajes del compilador. Delphi 7 aiiade tres advertencias del compilador adicionales: tipo inseguro, codigo inseguro, y conversion insegura. Estas advertencias se emiten en caso de operaciones que no se puedan utilizar para generar codigo "gestionado" seguro sobre la plataforma Microsoft .NET. Otra modification se encuentra relacionada con 10s nombres de unidad, que ahora pueden formarse con multiples palabras separadas por puntos, como en la unidad m a r c o . t e s t , almacenada en el archivo m a r c o . t e s t . p a s . Esta caracteristica ayudara a ofrecer soporte para espacios de nombres y para referencias de unidad mas flexibles en Delphi para .NET y las futuras ver- siones del compilador Delphi para Windows, per0 en Delphi 7 tiene un uso limi- tado. Clases y objetos Delphi se basa en 10s conceptos de la orientacion a objeto y, en particular, en la definition de nuevos tipos de clase. El uso de OOP esta forzado en parte por el entorno de desarrollo visual, ya que para cada formulario nuevo definido en tiempo de disefio, Delphi define automaticam'ente una clase nueva. Ademas, cada componente situado visualmente en un formulario es un objeto de un tip0 de clase disponible en la biblioteca del sistema o afiadido a ella. NOTA: Los tkrminos clase y objeto se utdizan con mucha fiecuencia y a rnenudo se confunden, ssi que asegurbmonos de estar de acuerdo sobre sus definiciones. Una clase es un tip0 de dabs definido por el usuario, que posee un estado (su representacibn o sus datos internos) y algunas opera- ciones (su comportamiento o sus mbtodos). Un objeto es una instancia de una clase o una variable del tipo de datos demdo por la clase. Los objetos son entidades rcales. ~ u a n d ~ e l programa se ejeiuta, los objetos ocipan park de la memoria para su repreaentacilln intern. La relacih en- objeto y clase es la misma que entre variable y tipo. Como en la mayor parte del resto de 10s lenguajes orientados a objetos (como Java y C#), en Delphi una variable de tipo clase no proporciona el almacenamien- to para el objeto, sino solo un punter0 o referencia al objeto en la memoria. Antes de utilizar el objeto, se debe reservar memoria para 61 mediante la creacion de una nueva instancia o asignando una instancia ya existente a la variable: var Obj 1, Obj2 : TMyClass; begin // a s i g n a r un o b j e t o r e c i e n c r e a d o Objl := TMyClass.Create; // a s i g n a r un o b j e t o e x i s t e n t e Obi2 : = ExistingObject; La llamada a create invoca un constructor predefinido disponible para cada clase, a no ser que la clase lo vuelva a definir (como ya veremos). Para declarar un nuevo tipo de datos de clase en Delphi, con algunos campos de datos locales y algunos metodos, se puede utilizar la siguicnte sintaxis: type TDate = class Month, Day, Year: Integer; procedure SetValue (m, d, y: Integer); function Leapyear: Boolean; end; h~ GE JUlU U W W I l V G U b l U l l WWir GI W l l l ~ l i W l , 1 ES &UlW 1CL14 CiVUlV cualquier otra), per0 es tan frecuente que respetarla harfr' que el d g o resulte intis facil de entender. qOTA:La convenci6n en Delphi es usar la letra T mmo prefijo para el lombre de cada clase que se escribe y cualquier otro tipo (T significa Tipo). ?,A, ,, ,'I, ..,, ,,--.,,, :*, I,,,, -1 ,,,:t,A,, 'Pa, ,'I ,,.,, I d , , ,,,, Un metodo se define con la palabra clave f u n c t i o n o p r o c e d u r e , segun si dispone de un valor de retorno o no. Dentro de la definicion de clase, solo se pueden definir 10s metodos; despues deben definirse en la seccion de implernentacion de la misma unidad. En este caso, se antepone a1 nombre de cada metodo el nombre de la clase a la que pertenece, mediante una notacion de puntos: procedure TDate.SetValue (m, d, y: Integer) ; begin Month : = m; Day := d; Year := y; end; function TDate.LeapYear: Boolean; begin // l l a m a I s L e a p Y e a r en S y s U t i l s . p a s Result : = IsLeapYear (Year) ; end; TRUCO: Si se pulsa Controi-Maylis-C mientras que el cursor se m'cw- tra sobre la definicion de clase, la ~aracteristiea Class Completion del editor de Delphi generara el esqueleto de la deWci6n d& 10s rn6bcbs declarados en una clase. Es asi como se puede usar un objeto de la clase definida anteriormente: var ADay: TDate; begin // c r e a u n o b j e t o ADay : = TDate.Create; t r~ // u s a e l o b j e t o ADay-SetValue ( 1 , 1 , 2000); if ADay-LeapYear then ShowMessage ( ' A d o b i s i e s t o : ' + IntToStr (ADay-Year)); finally // d e s t r u y e e l o b j e t o ADay. Free; end ; end : Fijese en que ADa y . LeapYear es una expresion similar a ADa y . Year, sin embargo, la primera es una llamada a una funcion y la segunda es un acceso direct0 a datos. Opcionalmente se pueden aiiadir parentesis tras la llamada a la funcion sin parametros. Se pueden encontrar 10s fragmentos de codigo anteriores en el codigo fuente del ejemplo Date 1, la unica diferencia es que el programa crea una fecha basada en el aiio que se introduce en un cuadro de edicion. Mas sobre metodos Hay mucho mas que comentar sobre 10s metodos. Estas son algunas breves notas sobre las caracteristicas disponibles en Delphi: Delphi soporta la sobrecarga de metodos. Esto significa que se pueden tener dos metodos con el mismo nombre, siempre que se marquen 10s meto- dos con la palabra clave o v e r l o a d y que las listas de parametros de 10s dos metodos Sean lo suficientemente diferentes. Mediante la comprobacion de 10s parametros. el compilador puede determinar que version se desea Ilamar. Los metodos pueden tener uno o mas parametros con valores predefinidos. Si estos parametros se omitiesen en la llamada a1 metodo, se asignaria el valor predefinido. Dentro de un metodo se puede usar la palabra clave self para acceder a1 objeto actual. Cuando se hace referencia a 10s datos locales del objeto, la referencia a s e l f es implicita. Por ejemplo, en el metodo s e t v a l u e de la clase TDa t e comentada anteriormente, se usa Month para hacer refe- rencia a un campo del objeto y el compilador transforma Month en S e l f .Month. Se pueden definir metodos de clase, indicados por la palabra clave c l a s s . Un metodo de clase no tiene una instancia de objeto sobre la que actuar, ya que puede aplicarse a un objeto de la clase o a la clase en su totalidad. Actualmente Delphi no tiene un mod0 de definir datos de clase, per0 puede simularse esta prestacion aiiadiendo datos globales en la porcion de implementacion de la unidad en que se defina a la clase. De manera predeterminada, 10s metodos usan la convencion de llamada r e g i s t er : 10s parametros (simples) y 10s valores de retorno se pasan del codigo de llamada a la funcion y de vuelta mediante registros de la CPU, en lugar de en la pila. Este proceso hace que las llamadas a metodo resul- ten mucho mas rapidas. Creacion de componentes de forma dinamica Para hacer hincapie en el hecho de que 10s componentes de Delphi no son muy distintos de otros objetos (y para demostrar el uso de la palabra clave S e l f ) , existe el ejemplo CreateCompos. Este programa tiene un formulario sin compo- nentes y un manejador para su evento OnMouseDown, escogido porque recibe como uno de 10s parametros la posicion del clic de raton (no como el evento O n c l i c k ) . Esta informacion es necesaria para crear un componente boton en esa posi- cion. Veamos el codigo de este metodo: procedure TForml.FormMouseDown (Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var Btn: TButton; begin Btn := TButton-Create (Self) ; Btn-Parent := Self; Btn-Left := X; Btn-Top : = Y; Btn-Width := Btn.Width + 50; Btn-Caption := Format ('Botdn en %d, % d l , [ X , Y]); end ; Con este codigo, se crean botones en las posiciones en las que se haga clic con el raton, como muestra la figura 2.1. En el codigo anterior, fijese en concreto en el uso de la palabra clave S e 1 f , tanto como parametro del metodo c r e a t e (para especificar el dueiio del componente), como valor de la propiedad P a r e n t . Figura 2.1. El resultado del ejernplo CreateCornps, que crea componentes boton en tiernpo de ejecucion. Cuando se escribe un procedimiento como el codigo que acabamos de ver, podriamos sentirnos tentados a utilizar la variable F o r m l en lugar de S e l f . En este ejemplo concreto, el cambio no tendria ninguna diferencia practica, per0 si existen diversas instancias de un formulario, usar F o r m l seria un error. De hecho, si la variable F o r m l se refiere a1 primer formulario de ese tipo que se ha creado, a1 pinchar sobre otro formulario del mismo tipo, el nuevo boton siempre aparecera en el primer formulario. Sus O w n e r y P a r e n t seran el F o r m l y no el formulario que ha pinchado el usuario. Por lo general, no conviene referirse a una instancia concreta de una clase cuando se necesita el objeto actual. Encapsulado Una clase puede tener cualquier cantidad de datos y cualquier numero de meto- dos. Sin embargo, para conseguir una buena tecnica orientada a objetos, 10s datos deberian estar ocultos o encapsulados dentro de la clase que 10s usa. Cuando se accede a una fecha, por ejemplo, no tiene sentido cambiar solo el valor del dia directamente. De hecho, si se cambia el valor del dia podria resultar una fecha no valida, como el 30 de febrero, por ejemplo. Si se usan metodos para acceder a la representacion interna de un objeto, se limita el riesgo de generar situaciones erroneas, puesto que 10s metodos pueden verificar si la fecha es valida y negarse a modificar el nuevo valor si no lo es. El encapsulado es importante porque permi- te que la persona que escribe las clases modifique la representacion interna en una version futura. El concepto de encapsulado se describe normalmente como una "caja negra", en la que no se conoce el interior: simplemente se sabe como interactuar con ella o como usarla, sin tener en cuenta su estructura. La parte "modo de empleo", denominada interfaz de clase, permite que otras partes de un programa tengan acceso y utilicen 10s objetos de dicha clase. Sin embargo, cuando se emplean 10s objetos, la mayor parte de su codigo esta oculto. Rara vez se conocen 10s datos internos que contiene el objeto y normalmente no hay manera de acceder directa- mente a 10s datos. Por supuesto, se supone que utilizamos 10s metodos para acce- der a 10s datos, que estan protegidos contra accesos no autorizados. Esta es la tecnica orientada a objetos del concepto de programacion clasico conocido como ocultacion de informacion. Sin embargo, en Delphi existe un nivel adicional de ocultacion, mediante propiedades. Delphi implementa este encapsulado basado en clases per0 todavia soporta el encapsulado clasico basado en modulos, que usa la estructura de unidades. Todo identificador que se declare en la seccion de interfaz de una unidad resulta visible a otras unidades del programa, siempre que se utilice una sentencia u s e s que se refiere a launidad que define el identificador. Por otro lado, 10s identificadores declarados en la seccion de implernentacion de la unidad seran locales a esa uni- dad. Privado, protegido y public0 En el caso de un encapsulado basado en clases, el lenguaje Pascal orientado a objetos tiene tres especificadores de acccso: p r i v a t e , p r o t e c t e d y p u b l i c . Un cuarto; p u b l i s h e d , controla la RTTI (informacion de tipo en tiempo de e.jecucion) y la informacion en tiempo de diseiio, proporcionando la misma dispo- nibilidad de cara a la programacion que si fuera p u b l i c . A continuacion se enumeran 10s tres especificadores de acceso clisi'cos: La directiva private: Denota campos y metodos de clase no accesiblcs fuera de la unidad (el archivo dc codigo fuente) que declara la clasc. La directiva protected: Se utiliza para indicar metodos y campos con visibilidad limitada. Solo la clase actual y sus clases heredadas pueden acceder a 10s elementos protegidos. Para ser mas precisos, solo la clase. las subclases y cualquier codigo presente en la misma unidad que la clase pueden acceder a 10s miembros protegidos. La directiva public: Denota campos y metodos a 10s que se puede acceder libremente desdc cualquier otra parte de un programa asi como en la uni- dad en la que se definen. Por lo general, 10s campos dc una clase deberian ser privados. Los metodos son normalmente publicos. Aunque esto no siempre es asi, 10s metodos pueden ser privados o protegidos si son necesarios internamente solo para realizar parte de un calculo. Los campos pueden ser protegidos para que se puedan manipular en subclases, aunque no se considera una buena practica de la orientation a objetos. ADVERTENCIA: Los especificadores de acceso solo restringen el acceso por parte del codigo que est i fuera de la unidad a ciertos miembros de clases declaradas en la parte de interfaz de la misma. Esto significa que si dos clases estAn en la misma unidad, sus campos privados no e s t h protegi- dos. Como e.jemplo, consideremos esta nueva version de la clase TDate: type TDate = class private Month, Day, Year: Integer; public procedure SetValue (y, m, d: Integer) ; overload; procedure SetValue (NewDate: TDateTime); overload; function Leapyear: Boolean; function GetText: string; procedure Increase; end; Se podria pensar en aiiadir otras funciones, como G e t D a y , G e t M o n t h y G e t Y ear, que simplemente devuelvan 10s datos privados correspondientes, per0 no siempre se necesitan funciones similares directas de acceso a datos. Si se conceden funciones de acceso para cada uno de 10s campos, se podria reducir el encapsulado y dificultar la modificacion de la implementacion interna de una clase. Las funciones de acceso deberian de definirse unicamente si forman parte de la interfaz logica de la clase que esta implementando. Otro nuevo metodo es el procedimiento Increase, que suma un dia a la fecha. Este calculo no es nada sencillo, porque hay que considerar las distintas longitudes de 10s meses asi como 10s 6 0 s bisiestos o no bisiestos. Lo que hare- mos, para que resulte mas sencillo escribir el codigo, sera cambiar la implementacion interna de la clase a1 tipo T D a t e T i m e de Delphi para la implernentacion interna. La definicion de clase cambiara a lo siguiente (el codigo completo aparece en el proximo ejemplo, D a t e p r o p ) : type TDate = class private fDate: TDateTime; public procedure SetValue (y, m, d: Integer); overload; procedure SetValue (NewDate: TDateTime); overload; function Leapyear: Boolean; function GetText: string; procedure Increase; end ; Fijese en que debido a que la unica modificacion se realiza en la seccion priva- daj no habra que modificar ninguno de 10s programas existents que usen la clase. Esa es la ventaja del encapsulado. NOTA: El tipo TDateTime es en realidad un numero de coma flotante. La parte entera del numero indica la fecha desde el 3O/l2/1899, la misma fecha basica usada por las aplicaciones OLE Automation y Win32. (Para exDresar 10s afios anteriores se usan valores ne~ativos.) La t>arte decimal - . indica la hora en forma de fiacci6n. Por ejemplo, un valor de 3,75 correspon- de a1 dos de enero de 1900, a las 6:00 de la tarde (tres cuartos de un dia). n--- _- - ---L-- r_ _L _ - -- J- ____--- - ---A_- -1 --1_- --- 2- _I-- ---- rara sumar o resrar recnas, se pueue sumar o restar el numero ue mas, que resulta d s sencillo que aiiadir &as con una representacih de dia/mes/aito. Encapsulado con propiedades Las propiedades son un mecanismo de orientacion a objetos muy sensato o una aplicacion practica muy bien pensada de la idea de encapsulado. Basicamente, se tiene un nombre que oculta por completo 10s datos de implernentacion. Esto per- mite modificar la clase ampliamente sin que afecte a1 codigo que la utiliza. Una buena definicion de propiedades es la de campos virtuales. Desde la perspectiva del usuario de la clase que las define, las propiedades poseen m a apariencia exactamente igual a la de 10s campos, ya que, por lo general se puede leer o escribir su valor. Por ejemplo, se puede leer el valor de la propiedad C a p t i o n de un boton y asignarla a la propiedad T e x t de un cuadro de edicion con el siguiente codigo: Parece que estuvidramos leyendo y escribiendo campos. Sin embargo, las pro- piedades pueden proyectarse directamente a datos, asi como a metodos de acceso, para leer y escribir el valor. Cuando las propiedades se proyectan a metodos, 10s datos a 10s que acceden pueden formar parte del objeto o estar fuera de el y pueden producir efectos secundarios, como volver a pintar un control tras haber cambiado sus valores. Tecnicamente, una propiedad es un identificador que esta proyectado a datos o metodos que usan una clausula r e a d y otra w r i t e . Por ejemplo, aqui tenemos la definicion de una propiedad M o n t h para una clase de fecha: property Month: I n t e g e r read FMonth write SetMonth; Para acceder a1 valor de la propiedad Month, el programa lee el valor del campo privado FMonth, mientras que para cambiar el valor de la propiedad llama a1 metodo S e t M o n t h (que ha de estar definido dentro de la clase, por supuesto). Son posibles diversas combinaciones (por ejemplo, un metodo para leer el valor o cambiar directamente un campo en la directiva w r i t e ) , per0 el uso de un metodo para cambiar el valor de una propiedad es muy comun. Estas son dos definiciones alternativas para la propiedad, proyectada sobre dos meto- dos de acceso o directamente sobre 10s datos en ambas direcciones: property Month: I n t e g e r read GetMonth write SetMonth; property Month: I n t e g e r read m o n t h write m o n t h ; Normalmente, 10s datos reales y 10s metodos de acceso son privados (o prote- gidos) mientras que la propiedad es publica. Esto significa que hay que usar la propiedad para tener acceso a aquellos metodos o datos, una tecnica que ofrece tanto la version simplificada como la extendida del encapsulado. Se trata de un encapsulado ampliado, porque no solo se puede cambiar la representacion de 10s datos y sus funciones de acceso, sino tambien aiiadir o eliminar funciones de acceso sin cambiar el codigo de llamada en absoluto. Un usuario solo necesita volver a compilar el programa usando la propiedad. TRUCO: Cuando se definen propiedades, se puede aprovechar la funcion Class C o m p l e t i o n del editor de Delphi. que se activa con la combina- . popiedad y el punto y coma, a1 pulsar Control-Mayus-C, Delphi propor- cionara una definicion completa y el esqueleto del mttodo de escritura. Si se escribe G e t delante del nombre del identificador despues de la pala- bta clave read tambitn se conseguira un m e t o d ~ de lectura sin apenas escribir. Propiedades de la clase TDate Como ejemplo, hemos aiiadido propiedades para acceder al aiio, el mes y el dia de un objeto de laclase T D a t e . Dichas propiedades no eskin proyectadas a campos especificos, sino al campo unico f D a t e que almacena toda la informa- cion de la fecha. Esta es la nueva definicion de la clase, con mejores metodos de lectura y escritura: t y p e TDate = c l a s s p u b l i c p r o p e r t y Year: In t ege r r e a d GetYear w r i t e SetYear: p r o p e r t y Month: I n t e g e r r e a d GetMonth w r i t e SetMonth; p r o p e r t y Day: In t ege r r e a d GetDay w r i t e SetDay; Cada uno de estos metodos se implementa facilmente utilizando funciones disponibles en la nueva unidad DateUtils. Veamos el codigo de dos de ellos (10s otros son muy similares): f u n c t i o n TDate.GetYear: In t ege r ; b e g i n Resul t := YearOf ( fDate) ; end; procedure TDate.SetYear(const Value: I n t e g e r ) ; b e g i n fDate : = Recodeyear ( fDate , Value) ; end ; El codigo de esta clase esta disponible en el ejemplo Dateprop. El programa utiliza una unidad secundaria para que la definicion de la clase T D a t e active el encapsulado y Cree un objeto de fecha simple guardado en una variable de formu- lario y almacenado en memoria durante toda la ejecucion del programa. Si se usa una tecnica estandar, el objeto se crea en el controlador de eventos oncreate del formulario y se destruye en el controlador de eventos OnDes t roy del formu- lario. El formulario del programa (vease figura 2.2) tiene tres cuadros de edicion y botones para copiar 10s valores de estos cuadros de edicion en las propiedades del objeto de fecha: Figura 2.2. El formulario del ejemplo Dateprop. ADVERTENCIA: Cuando se escribefi 10s valm; d $pn>&'ama utiliza el metodo setvalue en lugar & &£ink cada una de las pfopiedades. De hecho, asignar e1 mes y el &a por separado puede cam& pLoblemati cuando el mes no es valid0 para el dia en uso. Pongam~s por &&plo que la fecha actual es el 3 1 de enero jr qgxemos twignark el 20 de fe'bkro. Si asignamos primero el mes, esa pa& darsr error, puesto que el 3 1 d;e febrero no existe. Si asignamos primero el &a, el problem slirgilpr al hacer 1a asignstci6n inversa. Debido a las rm&m de validmikt para fixkttd, ss enejor.wigm.r todo a1 mismo ~~. Caracteristicas avanzadas de las propiedades Las propiedades tienen varias caracteristicas avanzadas. Este es un breve re- sumen de ellas: La directiva write de una propiedad se puede omitir, convirtiendola asi en una propiedad de solo lectura. El compilador dara error si intentamos cambiarla. Tambien se puede omitir la directiva read y definir una pro- piedad de solo escritura, per0 ese enfoque no tiene mucho sentido y no se suele emplear. El IDE de Delphi da un trato especial a las propiedades en tiempo de diseiio, que se declaran con el especificador de acceso pub1 i s hed y que por lo general aparecen en el Object Inspector para el componente selec- cionado. Las otras propiedades, normalmente denominadas propiedades solo de tiem- po de ejecucion, son las declaradas con el especificador de acceso public. Dichas propiedades pueden usarse en el codigo del programa. Se pueden definir propiedades basadas en matrices, que usan la notacion tipica con corchetes para acceder a un elemento de la lista. Las propieda- des basadas en la lista de cadenas, como Lines en un cuadro de lista, son un ejemplo tipico de este grupo. Las propiedades tienen directivas especiales, como stored y default, que controlan el sistema de streaming de componentes. incluso se pueden usar propiedades en expresiones, pero no siempre se puede pasar una propiedad como parimetro a un procedimiento o metodo. I Esto se debe a que una propiedad no es una posicion de memoria, por lo que no se puede utilizar como parimetro var u o u t : no se puede pasar por .' referencia. Encapsulado y forrnularios Una de las ideas clave del encapsulado es reducir el numero de variables globales cmpleadas por el programa. Se puede acceder a una variable global desde todas las partes de un programa. Por esa razon, un cambio en la variable global afecta al programa entero. Por otra parte, cuando se cambia la representacion de un campo de clase, solo hay que cambiar el codigo de algunos metodos de dicha clase y nada mas. Por lo tanto, podemos decir que la ocultacion de informacion se refiere a 10s cambios de encapsulado. Cuando tengamos un programa con diversos forrnularios, podemos hacer que algunos datos estkn disponibles para todos 10s formularios, si 10s declaramos como variable global en la parte de interfaz de la unidad de uno de 10s formula- rios: var F o r m 1 : T F o r m l ; n C l i c k s : I n t e g e r ; Esto funciona pero tiene dos inconvenientes. En primer lugar, 10s datos no estan conectados a un caso especifico del formulario, sino a1 programa entero. Si creamos dos formularios del mismo tipo, compartiran 10s datos. Si queremos que cada formulario del mismo tip0 tenga su propia copia de 10s datos, la unica solucion es aiiadirlos a la clase de formulario: type T F o r m l = class ( T F o r m ) public nClicks: Integer; end; Afiadir propiedades a formularios La clase anterior utiliza datos publicos, asi que por el bien del encapsulado, se la deberia modificar para que use datos privados y funciones de acceso a 10s datos. Una solucion aun mejor es aiiadir una propiedad a1 formulario Cuando sea necesario que alguna informacion del formulario este disponible en otros formu- larios, se deberia utilizar una propiedad. Simplemente hay que cambiar la decla- ration de campo del formulario, como se indica en el listado anterior, aiiadir la palabra clave p r o p e r t y delante de ella y a continuacion, pulsar Control-Mayus- C para activar la funcion Code Comple t ion. Delphi generara automaticamente todo el codigo adicional necesario. El codigo completo para esta clase de formulario esta disponible en el ejemplo FormProp y la figura 2.3 muestra el resultado. El programa puede crear multiples instancias del formulario (es decir, multiples objetos basados en la misma clase de formulario), cada una con su propia cuenta de clic. Figura 2.3. Dos forrnularios del ejemplo ForrnProp en tiernpo de ejecucion. 4 NOTA: ~ i ~ c s e eg quo el a & d ~ p & i ~ d d a d pun f o n d ario, no 3. Mia& -~4' a'la M a de ~ ~ ~ e s del f a h d ! del Qbject Bpector . Conviene usar las propiedades tambien en las clases de formulario para encapsular el acceso a 10s componentes de un formulario. Por ejemplo, si hay un formulario principal con una barra de estado en la que se muestre cierta informa- cion (y con la propiedad s i m p l e p a n e l definida como T r u e ) y hay que modi- ficar el texto de un formulario secundario, podriamos sentir la tentacion de escribir: Form1 .StatusBarl .SimpleText : = ' n u e v o texto' ; Esta es una costumbre muy comun en Delphi, pero no es una buena costumbre, porque no ofrece encapsulado de la estructura de formulario ni de sus componen- tes. Si hay un codigo similar en una aplicacion y mas tarde se decide modificar la interfaz de usuario del formulario (y reemplazar S t a t u s B a r por otro control o activar diversos paneles), habra que adaptar el codigo en muchos sitios. La alter- nativa es utilizar un metodo o, incluso mejor, una propiedad para ocultar un control concreto. Esta propiedad puede definirse como: property StatusText: string read GetText write SetText; siendo G e t T e x t y S e t T e x t metodos que leen de y escriben en la propiedad S i m p l e T e x t de la barra de estado (o la etiqueta de uno de sus paneles). En 10s demas formularios del programa simplemente se puede hacer referencia a la pro- piedad StatusText del formulario y si la interfaz de usuario cambia, solo se veran afectados 10s metodos de lectura y escritura. Constructores Para asignar la memoria a1 objeto, podemos llamar a1 metodo Create. Este es un constructor, un metodo especial que podemos aplicara una clase para asignar memoria a una instancia de dicha clase. El constructor devuelve la instan- cia, que puede asignarse a una variable para almacenar el objeto y usarlo mas tarde. El constructor por defecto TObj e c t . Create inicializa todos 10s datos del nuevo caso a cero. Para que 10s datos de dicho caso comiencen con un valor diferente a cero, hay que escribir un constructor personalizado. El nuevo constructor se puede denominar c r e a t e o tener otro nombre, y hay que usar la palabra clave constructor delante de el. Fijese en que no es necesario llamar a TOb j e c t . Create: es Delphi el que asigna memoria para el nuevo objeto, no el constructor de clase. Todo lo que hay que hacer es iniciar la base de clase. Aunque se puede usar cualquier nombre para el constructor, deberia ajustarse a1 nombre estandar, c r ea t e . Si se usa otro nombre distinto de c rea te , el constructor Create de la clase basica TOb j e c t aun estara disponible, per0 un programador que llame a1 constructor por defecto podria pasar por alto el codigo de inicializacion ofrecido porque no reconoce el nombre. A1 definir un constructor c r e a t e con algunos pa rhe t ros , reemplazamos la definicion predeterminada por una nueva y hacemos que su uso resulte obligato- rio. Por ejemplo, despues de haber definido: type TDate = class public constructor Create (y, m, d: Integer); solo podremos llamar a este constructor y no a1 c r ea t e estandar: var ADay: TDate; begin // Error, no conpila: ADay := TDate.Create; // OK: ADay : = TDate.Create (1, 1, 2000); Las normas de escritura de constructores para componentes personalizados son diferentes. La razon es que en este caso hay que sobrescribir un constructor virtual. La sobrecarga resulta particularmente importante para 10s constructores ya que se pueden aiiadir multiples constructores a una clase y llamarlos a todos ellos create. Este enfoque hace que 10s constructores resulten faciles de recor- dar y sigan una via estandar proporcionada por otros lenguajes de orientacion a objetos en 10s que 10s constructores deben de tener todos el mismo nombre. Como ejemplo, podemos aiiadir a la clase dos constructores create distintos; uno sin parametros, que oculta el constructor predeterminado; y otro con valores de inicializacion. El constructor sin parametros usa el valor predefinido de la fecha de hoy (como se puede ver el codigo completo del ejemplo Dataview): '=we TDate = c l a s s p u b l i c c o n s t r u c t o r Crea te ; overload; c o n s t r u c t o r Create (y , m, d : I n t e g e r ) ; overload; Destructores y el metodo Free Del mismo mod0 que una clase puede tener un constructor personalizado, tambien puede tener un destructor personalizado, un mCtodo declarado con la palabra clave destructor y llamado Destroy. A1 igual que una llamada a1 constructor asigna memoria para el objeto, un destructor libera la memoria. Los destructores son necesarios solo para objetos que adquieren recursos externos en sus constructores o durante su vida util. Se puede escribir codigo personalizado para un destructor, en general sobrescribiendo el destructor Destroy predeter- minado, para permitir que un objeto ejecute algo de codigo de limpieza antes de su destruccion. Destroy es un destructor virtual de la clase TObj ect. Jamas deberia definirse un destructor distinto, ya que 10s objetos suelen destruirse me- diante una llamada al metodo Free y este metodo llama al destructor virtual Destroy de la clase especifica. Free es un metodo de la clase TOb j ect, heredado por todas las demas clases. El metodo Free verifica basicamente si el objeto actual (Self) no es nil antes de llamar a1 destructor virtual Destroy. Free no cambia el objeto a nil automaticamente, sino que es algo que se debe- ria hacer personalmente. La razon es que el objeto no sabe que variables pueden referirse a el, por lo que no hay mod0 de cambiarlas todas a nil. Delphi 5 present6 un procedimiento FreeAndNil que se puede usar para liberar un objeto y dar el valor nil a su referencia a1 mismo tiempo. Se puede llamar a FreeAndNil (Ob j 1 ) en lugar de escribir lo siguiente: 0 b j l . F r e e ; Objl := n i l ; El modelo de referencia a objetos de Delphi En algunos lenguajes orientados a objetos, a1 declarar una variable de un tipo de clase, se crea una instancia de dicha clase. Delphi, en cambio, se basa en un modelo de referencia a objetos. La idea es que una variable de un tipo de clase, como la variable TheDay en el ejemplo anterior ViewDate, no mantiene el valor del objeto. En lugar de eso, contiene una referencia, o un puntero, para indicar la posicion de mernoria en la que se ha almacenado el objeto. Se puede ver la estructura en la figura 2.4. TheDay objeto TDay Figura 2.4. Una representacion de la estructura de un objeto en memoria, con una variable que se refiere a el. El unico problema de esta tecnica es que cuando se declara una variable, no se crea un objeto en memoria (lo que es inconsistente con el resto de variables, confundiendo a 10s nuevos usuarios de Delphi); solo se reserva la posicion de memoria para una referencia al objeto. Las instancias de objetos h a b r h de crear- se manualmente, a1 menos para 10s objetos de las clases que se definan. Las instancias de 10s componentes que se coloquen en un formulario son creadas automaticamente por la biblioteca de Delphi. Hemos visto como crear una instancia de un objeto, aplicando un constructor a su clase. Cuando hayamos creado un objeto y hayamos terminado de usarlo, es necesario eliminarlo (para evitar llenar la rnemoria que ya no necesita, lo cual origina lo que se conoce como "goteo de memoria"). Esto se puede hacer mediante una llamada a1 metodo Free. Siernpre que se creen objetos cuando Sean necesa- rios y se liberen cuando ya no lo Sean, el modelo de referencia a objetos funciona- ra perfectamente. El modelo de referencia a objetos tiene una gran influencia en la asignacion de objetos y en la administracion de memoria. Asignacion de objetos Podemos preguntarnos que ocurriria si una variable que mantiene un objeto solo contiene una referencia a1 objeto en memoria y se copia el valor de dicha variable. Supongamos que escribimos el metodo Bt nToda yCli c k del ejemplo ViewDa te del siguiente modo: procedure TDateForm.BtnTodayClick(Sender: TObject); var NewDay: TDate; begin NewDay := TDate-Create; TheDay := NewDay; LabelDate.Caption := TheDay-GetText; end; Este codigo copia la direccion de memoria del objeto NewDay a la variable TheDay (corno muestra la figura 2.5); no copia 10s datos de un objeto en el otro. En esta circunstancia concreta, esta tecnica no es muy adecuada, puesto que cada vez que se pulsa el boton, se asigna memoria para un nuevo objeto y nunca se libera la memoria del objeto a la que anteriormente apuntaba la variable TheDay. NewDay objeto TDate TheDay Figura 2.5. Una representacion de la operacion de asignacion de una referencia de objeto a otro objeto. Esto es distinto de copiar el contenido real de un objeto en otro. Esta cuestion especifica puede resolverse liberando el objeto antiguo, como en el siguiente codigo (que tambien esta simplificado, sin el uso de una variable explicita para el objeto de nueva creacion): procedure TDateForm.BtnTodayClick(Sender: TObject); begin TheDay-Free; TheDay : = TDate.Create; Lo importante es que cuando se asigna un objeto a otro objeto, Delphi copia la referencia a1 objeto en memoria en la nueva referencia a objeto. No deberia consi- derarse esto como algo negativo: en muchos casos, ser capaz de definir una varia- ble que se refiera a un objeto ya existente puede ser una ventaja. Por ejemplo, se puede almacenar el objeto devuelto a1 acceder a una propiedad y usarla en las sentencias siguientes, como se indica en este fragment0 de codigo: var ADay: TDate; begin ADay: User1nformation.GetBirthDate;// usar un ADay Lo mismo ocurre si se pasa un objeto como parametro a una funcion: no se crea un nuevo objeto, sino que se hace referencia a1 mismo en dos lugares diferen- tes del codigo. Por ejemplo, a1 escribir este procedimiento y llamarlo como se indica a continuacion, se modificara la propiedad C a p t i o n del objeto B u t t o n l , no de una copia de sus datos en memoria (algo que seria completamente inutil): procedure Captionplus (Button: TButton); begin Button.Caption := Button.Caption + ' + I ; end; // llamar.. . CaptionPlus (Buttonl) Esto significa que el objeto se pasa por referencia sin el uso de la palabra clave var y sin ninguna otra indicacion obvia de la semantica de paso por referencia, lo que confunde a 10s novatos. Cabria preguntarse lo que sucede si realmente se quieren cambiar 10s datos de un objeto existente, para que se corresponda con 10s datos de otro objeto. En este caso, hay que copiar cada campo del objeto, lo cual es posible solo si son todos publicos, u ofrecer un metodo especifico para copiar 10s datos internos. Algunas clases de la VCL tienen un metodo Assign, que realiza esta operacion de copia. Para ser mas precisos, la mayoria de las clases de la VCL que heredan de TPers is tent, per0 no de TComponent, tienen el metodo Ass ign. Otras clases derivadas de TComponent tienen este metodo per0 lanzaran una excepcion cuando se llama. En el ejemplo Da t eCopy, se ha aiiadido un metodo Assign a la clase TDa te y se le ha llamado desde el boton Today, con el siguiente codigo: procedure TDate .Assign (Source: TDate) ; begin fDate := Source.fDate; end ; procedure TDateForm.BtnTodayClick(Sender: TObject); var NewDay: TDate; begin NewDay : = TDate-Create; TheDay .Assign (NewDay) ; LabelDate.Caption := TheDay.GetText; NewDay.Free; end ; Objetos y memoria La administracion de memoria en Delphi esta sujeta a tres normas, a1 menos si se permite que el sistema trabaje en armonia sin violaciones de acceso y sin consumir memoria innecesaria: Todo objeto ha de ser creado antes de que pueda usarse Todo objeto ha de ser destruido tras haberlo utilizado. Todo objeto ha de ser destruido solo una vez. El tener que realizar estas operaciones en el codigo o dejar que Delphi controle la administracion de memoria, dependera del modelo que escojamos entre las distintas tecnicas que ofrece Delphi. Delphi soporta tres tipos de administration de memoria para elementos dinamicos: Cada vez que creamos un objeto explicitamente en el codigo de una aplica- cion, tambien debemos liberarlo (con la sola excepcion de un puiiado de objetos del sistema y de objetos que se utilizan a traves de referencias de interfaz). Si no se hace asi, la memoria utilizada por dicho objeto no se libera hasta que finaliza el programa. Cuando creamos un componente, podemos especificar un componente pro- pietario, pasando el propietario a1 constructor del componente. El compo- nente propietario (normalmente un formulario) se transforma en el responsable de destruir todos 10s objetos que posee. Asi, si creamos un componente y le damos un propietario, no es necesario que nos acordemos dc destruirlo. Este es el comportamiento estandar de 10s componentes que se crean en tiempo de diseiio a1 colocarlos sobre un formulario o modulo de datos. Sin embargo, es imperativo que se escoja un propietario cuya des- truccion quede garantizada; por ejemplo, 10s formularios suelen pertenecer a 10s objetos globales A p p l i c a t i o n , que son destruidos por la bibliote- ca cuando acaba el programa. Cuando la RTL de Delphi reserva memoria para las cadenas y matrices dinamicas, libera automaticamente la memoria cuando la referencia resul- ta inalcanzable. No es necesario liberar una cadena: cuando resulta inacce- sible, se libera su memoria. Destruir objetos una sola vez Otro problema es que si se llama a1 metodo F r e e (o a1 destructor D e s t r o y ) de un objeto dos veces, dara error. Sin embargo, si recordamos cambiar el objeto a n i l , se puede llamar a F r e e dos veces sin ningun problema. b - NOTA: Podriamos preguntarnos por que se puede llamar a Free con total seguridad si la referencia del objeto es n i l , pero no se pue& llamar a D e s t r o y . La razon es que F r e e es un mbodo conocido en una posicibn de memoria dada, rnientras que la funcion virtual Destroy se defrne en tiempo de ejecucion a1 ver el tip0 de objeto, una operacibn muy peligrosa si el objeto ya no existe. Para resumir todo esto, hemos elaborado una lista de directrices: Llamar siempre a F r e e para destruir objetos, en lugar de llamar a1 des- tructor D e s t r o y . Utilizar F r e e A n d N i 1 o cambiar las referencias de objeto a n i 1 despues de haber llamado a F r e e , a no ser que la referencia quede inmediatamente despues fuera de alcance. En general, tambien se puede verificar si un objeto es nil usando la funcion signed. Por lo que las dos sentencias siguientes son equivalentes, a1 menos la mayor parte de 10s casos: i f Assigned (ADate) then . . . i f ADate <> nil then . . . Fijese en que estas sentencias solo verifican si el puntero no es nil, no verifi- can si se trata de un puntero valido. Si se escribe el siguiente codigo, se realizara la verificacion, per0 se obtendra un error en la linea de llamada a1 metodo del objeto: ToDestroy.Free; i f ToDestroy <> n i l then ToDestroy.DoSomething; Es importante darse cuenta de que llamar a F r e e no cambia el objeto a nil. Herencia de 10s tipos existentes Normalmente es necesario usar una version ligeramente diferente de una clase existente. Por ejemplo, se podria necesitar aiiadir un metodo nuevo o modificar ligeramente uno dado. Si se copia y se pega la clase original y se modifica (una alternativa terrible, a no ser que exista una razon especifica para hacer esto), se duplicara el codigo, 10s errores y 10s dolores de cabeza. En lugar de esto, se deberia utilizar una caracteristica clave de la programacion orientada a objetos: la herencia. Para heredar de una clase existente en Delphi, solo hay que indicar esa clase a1 principio de la declaracion de la nueva clase. Por ejemplo, esto se hace cada vez que se crea un formulario: tYPe TForml = c l a s s (TForm) end; Esta definicion indica que la clase T F o r m l hereda todos 10s metodos, cam- pos, propiedades y eventos de la clase TForm. Se puede llamar a cualquier meto- do public0 de la clase T F o r m para un objeto del tipo T F o r m l . TForm, a su vez, hereda algunos de sus metodos de otra clase, y asi sucesivamente hasta la clase basica TOb j ec t . Como ejemplo de herencia, podemos cambiar una nueva clase a partir de T D a t e y modificar su funcion G e t T e x t . Se puede encontrar este codigo en la unidad Date del ejemplo NewDate: tYPe TNewDate = c l a s s (TDate) pub1 i c function GetText: string; end : Para implementar la nueva version de la funcion GetText, utilizamos la funcion Format DateTime, que emplea (entre otras caracteristicas) 10s nom- bres de mes predefinidos disponibles en Windows, estos nombres dependen de la configuracion regional del usuario y de la configuracion del lenguaje. Muchas de estas configuraciones las copia Delphi en constantes definidas en la biblioteca, como LongMonthNames, ShortMonthNames y muchas otras que puede encontrar bajo el tema "Currency and datehime formatting variables" (Variables para formatear la moneda y la fecha/hora) en el archivo de ayuda de Delphi. Veamos el metodo GetText, en el que 'dddddd' corresponde a1 formato de fecha largo: function TNewDate.GetText: string; begin GetText : = FormatDateTime ( ' d d d d d d ' , fDate); end : TRUCO: Cuando usamos la information regional, el programa NewDate se adapta automaticamente% las diferentes codi@r&i~nes & u~arici de Windows. Si ejecuta este mismo programa en un ordenador con una confi- guracion regi~nal en ,espaiiol, 10s nombresde 10s m e s a agsuecerh automaticamente en eqpat(o1 Cuando tengamos la definicion de la nueva clase, hay que usar este nuevo tipo de datos en el codigo del formulario del ejemplo NewDate. Simplemente hay que definir el objeto TheDay de tipo TNewDate y crear un objeto de la nueva clase mediante en el metodo Formcreate. No es necesario modificar el codigo con llamadas de metodo, ya que 10s metodos heredados seguiran funcionando del mismo modo; sin embargo, se modifica su efecto, como muestra la nueva salida (vease figura 2.6) jueves, 25 de dlclembre de 2003 Figura 2.6. El resultado del programa NewDate, con el nombre del mes y del dia de acuerdo con la configuracion regional de Windows. CCI;I Campos protegidos y encapsulado El codigo del mktodo GetText de la clase TNewDate compila solo si esta escrito en la misma unidad que la clase TDate. De hecho, accede a1 campo privado f Date de la clase ascendiente. Si queremos colocar una clase descen- diente en una unidad nueva, debemos declarar el campo fDate como protegido o aiiadir un metodo de acceso protegido en la clase ascendiente para leer el valor del campo privado. Muchos desarrolladores creen que la primera solucion es siempre la mejor, ya que declarar la mayor parte de 10s campos como protegidos permitira que una clase resulte mas extensible y hara mas sencillo escribir clases heredadas. Sin embargo, este enfoque se enfrenta con la idea del encapsulado. En una gran jerar- quia de clases, modificar la definicion de algunos campos protegidos de las clases base resulta tan dificil como modificar algunas estructuras globales de datos. Si diez clases derivadas acceden a estos datos, modificar su definicion significa modificar potencialmente el codigo de cada una de estas 10 clases. La flexibilidad, extension y encapsulado normalmente son objetivos conflicti- vos, por lo que deberiamos favorecer el encapsulado, sin sacrificar la flexibili- dad. Normalmente eso se puede conseguir usando un metodo virtual. Si se decide no utilizar el encapsulado para que la codification de las subclases sea mas rapi- da, el disefio podria no ajustarse a 10s principios de la orientacion a objetos. Acceder a datos protegidos de ottas clases Hemos vim que en Delphi, 10s datos privados y p r i P t e ~ 1 3 ~ de una elase son accesibles para cualquier funcib o rnM~ que aparezca ed fa rnismti unidad qae l a dase. Por ejemplo. ~ ~ ~ i d e r e r n i j ~ esta'clase f ~ & del ejem plo Protec&~): t Y P 9 TTest = ex-s ptotec ted ~ r o t e k t e d ~ I k B ? Integer; d; Cued0 hayarnos'co~ocado esta clase qn Ia unidad, no se podra accedet absu park protegida directamente desde otras unidades. Segiin esto, si escribi- mm el kipiente cbdigo. p~ocecbre TForrnl. B u t C o n l C & i c R (Sender : T O b j e c t ) ; VaE Ob'j: TTest; .begin Obj : = TTest . C r e a t e : O b ] . ProtectedData := 20; // no va a compiler 'ProtectedDatan' (Identificador no declarado: Datos Protegidos). En este momento, se podria pensar que no hay manera de acceder a 10s datos prote- gidos de una clase defmida en una unidad diferente. Sin embargo, en cierto mod0 si se puede. Tengarnos en cuenta lo que ocurre si se crea una clase aparentemente derivada inutil, corno: type TTestHack = alaas (TTest); Ahora, si realizamos una conversion directa del objeto a la nueva clase y accedemos a 10s datos protegidos a traves de ella, el codigo sera: var Obj: TTest; begin Ob j : = TTest. Create; TTestHack (Obj) .ProtectedData := 20; // ;conpila! csre coaigo compua y runciona correctamenre, como se pueae ver si se ejecuta el programa Protection. La raz6n es que la clase TTestHack hereda autornstticamente 10s campos protegidos de la clase birsica TTest y, como est4 en la misma unidad que el ckligo que intenta acceder a 10s datos de 10s campos heredados, 10s datos protegidos resultan accesibles. Como seria de esperar, si se mueve la declaracibn de la clase TTes t Hac k a una unidad secundaria, el programa ya no cornpilad. Ahora que ya hemos visto d m o se hace, hay que Wet en cuenta que viola el mecanismo de proteccibn de c lam de este m o b pbdrfir Brigbar errores en el programa (a1 acceder a datos a 10s que no deberimw tener acceso) y no respeta las tbcnicas de orientacih a objetos. Sin embargd, en muchas ocasiones usar esta thnica es la mejor solucibn, como veremos a1 analizar el codigo fuente de Ia VCL y el ddigo fuente de mucbos wmponentes Delphi. Dos ejemplos de ello son el acceso a la propiedad Text de la clase TControl y las posicio&s Row y G o 1 del control DBGrid. EQtas dos ideas aparecen en-10s ejemplos Text Prop y DBGridCol, respectiva- mente. (Son ejemplos bastante avanzados, asi que es mejor no enfientarse a ellos hasta tener un buen conocimiento de Delphi.) Aunque el primer0 es un :̂--I- ---a --L 1 - 1-1 ---- A- 1- ----.---:I- 3- A:--- ---A LA- -i -:---la ~jcruylo r ~ ~ u ~ e u c ~ WQ UG IU z;onvwsrun oe upus wwX;sr, cl qcmplu DBGrid de Row y Col es en &dad un ejemplo de w o opuesto. que ilustplos riwgm de a c d r a bits que la persona que escribib las clases prefifio s o exgoner. La fib y colbima de una clase DB-id no significan lo mismo gw en u& ~ r p w ~ r i d Q una StringGrid &q clpses bhi- cas). En pjmer h p r , ll5Gxld ao cuenta las w b @as oomo cedas rcakr ( & t i a d i s eelda? dehatos dc 10s c l ~ ~ d e ~ b r a t i l i q ] , ~ P ~ r lo que sys indices ,de fils. y aoluraaa. €idrib qua aj$#arse a los,efemento# biar sin que nos demos cuenta). En segundo lugar, la DBGrid es una vista virtual de 10s datos. Cuando nos desplazamos hacia arriba en una DBGrid, 10s dabs pueden moverse bajo ella, bero la fila seleccionada en ese momen- to podria no cambiar. protegidos miembros de una clase) se describe normalmente como un hack o apaKo y deberia evitarse siempre que sea posible. El problema no esth en acceder a datos protegidos de una cIase en la misma unidad sino en declarar . , . - . .. . . . una clase con el unico tin de acceder a datos protegldos de un ObJetO exls- tente de una clase distinta. El peligro de esta tecnica esth en la conversion de tipos codificada directamente de un objeto de una clase a otra diferente. Herencia y compatibilidad de tipos Pascal es un lenguaje con tipos estrictos. Esto significa que no se puede, por ejemplo, asignar un valor entero a una variable booleana, a no ser que se aplique una conversion de tipos explicita. La regla es que dos valores son compatibles en tip0 so10 si son del mismo tipo de datos o (para ser mas precisos) si su tipo de dato se rcfiere a una unica definicion de tipo. Para simplificarlo todo, Delphi hace que algunas asignaciones de tipos predefinidas Sean compatibles: se puede asignar un Extended a un Double y viceversa: con promocion o degradacion automatica (y una potencial perdida de precision). - - - - - - - ADVERTENCIA: Si se redefine el mismo tip0 de datos en dos unidades diferentes, no s e r h compatibles, incluso aunque sus nombres Sean identi- cos. SerA muy dificil compilar y depurar un programa que use dos tipos con el mismo nombre de dos unidades diferentes. Existe una importante excepcion a esta norma en el caso de 10s tipos de clase. Si se declara una clase, como TAnimaL, y se deriva de ella una nueva clase, como por cjemplo TDog, se puede asignar un objeto de tipo TDog a una variable de tipo TAnimal. Esto se debe a que un perro (dog) es un animal. Como regla general, se puede usar un objeto de una clase descendente cada vez que se espere un objeto de la clase ascendente. Sin embargo, lo opuesto no resulta legal; no se puede usar un objeto de una clase antecesora cuando se espera un objeto de una clase que desciende de la anterior. Para simplificar la esplicacion, veamos este codigo: var MyAnimal : TAnimal; MyDog: TDog; begin MyAnimal : = MyDog; // Esto es correcto MyDog : = MyAnimal; // ;Esto es u n error! Enlace posterior y polimorfismo Las funciones yprocedimientos de Pascal se basan normalmente en el enlace estatico o anterior. Esto significa que el compilador y el enlazador resuelven una llamada a un metodo, que reemplazan la peticion por una llamada a la posicion de memoria especifica en la que se encuentra la funcion o el procedimiento (esto se conoce como la direccion de la funcion). Los lenguajes de orientacion a objetos permiten el uso de otra forma de enlace, conocida como enlace dinamico o poste- rior. En este caso, la direccion real del metodo a llamar se establece en tiempo de ejecucion, segun el tipo de instancia utilizada para hacer la llamada. Esta tecnica se conoce como polimorfismo (que en griego significa muchas formas). Polimorfismo significa que se puede llamar a un metodo, aplicarlo a una variable, per0 que el metodo a1 que realmente llama Delphi depende del tipo de objeto con el que este relacionada la variable. Delphi no puede decidir la clase real del objeto a1 que se refiere la variable hasta estar en tiempo de ejecucion, debido a la norma de la compatibilidad de tipos. La ventaja del polimorfismo es que permite escribir codigo mas simple, tratar tipos de objetos distintos como si se tratara del mismo y conseguir el comportamiento correcto en tiempo de ejecu- cion. Por ejemplo, supongamos que una clase y una clase heredada (las clases TAnimal y TDog) definen ambas un nuevo metodo y que este metodo tiene enlace posterior o tardio. Se puede aplicar este metodo a una variable generica como MyAnimal que en tiempo de ejecucion puede referirse a un objeto de la clase TAnimal o a un objeto de la clase TDog. El metodo real a llamar se determina en tiempo de ejecucion, segun la clase del objeto real. El ejemplo PolyAnimals muestra esta tecnica. Las clases TAnimal y TDog tienen un metodo Voice, que pretende reproducir el sonido que realiza el animal seleccionado, como texto y como sonido (mediante una llamada a la funcion Playsound de la API definida en la unidad MMSystem). Este metodo se define como virtual en la clase TAnimal y mas tarde se sobrescribe cuando se define la clase TDog, mediante el uso de las palabras clave virtual y override: type TAnimal = class public function Voice: string; virtual; TDog = class (TAnimal) public function Voice: string; override; El efecto de la llamada M yAnimal . Voice puede variar. Si la variable MyAnimal se refiere en un momento dado a un objeto de la clase TAnimal, llamara a1 metodo TAnimal . Voice. Si se refiere a un objeto de la clase TDog, llamara en cambio a1 metodo TDog . Voice. Esto ocurre solo porque la funcion es virtual (como veremos si se elimina esta palabra clave y se vuelve a compilar). La llamada a MyAnima1. Voice funcionara en el caso de un objeto que sea una instancia de cualquier descendiente de la clase TAnimal, aunque las clases esten definidas en otras unidades, o aunque todavia no se hayan escrito. El compilador no necesita conocer todos 10s descendientes para hacer que la llamada sea compatible con ellos, solo se necesita la clase ascendiente. En otras palabras, esta llamada a MyAnima 1 . Voice es compatible con todas las futuras clases que hereden de TAnimal. - - - . - . - . . . . . - - - - - . - . .- - - -. . - . I NOTA: Esta es la raz6n clave por la que 10s lenguajes de programa&n I orientada a obietos favorecen la reutilizacion. Se vuede escribir un cbdiqo i- [Y GI prugriu~laj iuuavla sc yucuc iulrpllar, aunquc sc n a y u GSGIILU IIIIICS de lineas de c d i g o que la usan. Por supuesto, existe una condicion: las clases ascendientes de la jerarquia ban de disekrse con mucho cuidado. En la figura 2.7 se puede ver un ejemplo dc la salida del programa PolyAnimals. A1 ejecutarlo, se oiran 10s sonidos correspondientes producidos por la llamada a Playsound. - Figura 2.7. El resultado del ejemplo PolyAnimals. Sobrescribir y redefinir metodos Como acabamos de ver, para sobrescribir un metodo con enlace posterior en una clase descendiente, hay que usar la palabra clave override. Fijese en que esta solo puede utilizarse si se definio el metodo comovirtual (o dinamico) en la clase ascendiente. Las normas son sencillas: un metodo definido como estatico sigue siendo esta- tico en todas sus subclases, a no ser que se oculte con un nuevo metodo virtual que tenga el mismo nombre. Un metodo definido como virtual, sigue manteniendo el enlace posterior de cada subclase (a menos que se oculte con un metodo estati- co, que resulta algo bastante alocado). No hay ningun mod0 de cambiar este comportamiento, debido a la forma en que el compilador genera un codigo dife- rente para 10s metodos con enlace posterior. Para redefinir un metodo estatico, hay que aiiadir un metodo a una subclase que tenga 10s mismos parametros o parametros diferentes que el original, sin ninguna otra especificacion. Para sobrescribir un metodo virtual, habra que especificar 10s mismos parametros y usar la palabra clave o v e r r i d e : type TMyClass = class procedure One; virtual; procedure Two; (metodo estdtico) end; TMyDerivedClass = class (TMyClass) procedure One; override; procedure Two; end; Hay dos formas muy comunes de sobrescribir un metodo. Una consiste en reemplazar el metodo de la clase ascendiente por una nueva version. La otra, en aiiadir mas codigo a1 metodo existente. Para ello se utiliza la palabra clave i n h e r i t e d que llama a1 mismo metodo de la clase ascendiente. Por ejemplo, se puede escribir: procedure TMyDerivedClass.0ne; begin / / codigo nuevo . . . / / llamada a 1 procedimiento MyClass.One inherited One ; end; Cuando se sobrescribe un metodo virtual existente de una clase basica, hay que usar 10s mismos parametros. Cuando se presenta una nueva version de un metodo en una clase descendiente, se puede declarar con cualquier parametro. De hecho, este sera un nuevo metodo independiente del metodo ascendiente del mis- mo nombre, solo que tendra el mismo nombre. Veamos un ejemplo: type TMyClass = class procedure One; end; TMyDerivedClass = class (TMyClass) procedure One ( S : string) ; end; NOTA: Si se usan las definiciones de clase anteriores, cuando se crea un objeto de la clase TMyDer ivedClas s, se puede usar su m&odo One con 3 3 . . . . 4 _ _ 1 L ! I A . , 1 I - el parametro ae cauena, pero no la version sm parametros aeImaa en la clam bit8iaa. Qi se necesita esto, se puede marcar el metodo redeclarado (el de h c h derivada) con la palabra clave overload. Si el &todo time pariimetros diferentes a 10s de la versibn de la clase bbica, se cunvierte cfedtivamente en un mktodo sobrecargado. Si no es asi, reemplaza a1 rnkto- do ck la olase bbsica. Ffjese en que el m&odo no necesita estar marcado con overload en la c h e bkica. Sin embargo, si el m h d o de la chse bhica es virtual, el compihd~r emite la advertencia "Method 'One'hfdes virtual method of base type "~yClass" '(E1 m&odo 'One'oculta el metodo virtual del tip bbico "TMyClassW). Para evitar este mensaje e instruir a1 compilador de forma m h precisa sobre nuestras intenciones, se puede usar la directiva reintroduce. El c6digo sobre este tema se puede encontrar en el ejem- plo Reintr. Metodos virtuales frente a metodos dinamicos En Delphi, hay dos formas distintas de activar el enlace posterior. Se puede declarar el metodo como virtual, como hemos visto antes, o como dinamico. La sintaxis de estas dos palabras clave (virtual y dynamic) es exactamente la misma y el resultado de su uso tambien. Lo que cambia es el mecanismo interno usado por el compilador para implementar el enlace posterior. Los metodos virtuales se basan en una tabla de metodos virtuales (VMT, tam- bien conocida como vtable), que es una matriz de direcciones de metodo. Para una llamada a un metodo virtual, el compilador genera codigo para saltar a una direc- cion almacenada en la enesima ranura de la tabla de metodos virtuales del objeto. Las tablas de metodo virtualpermiten que las llamadas a metodo se ejecuten rapidamente, pero se necesita una entrada para cada metodo virtual de cada clase descendiente, aunque el metodo no se sobrescriba en la subclase. Las llamadas a un metodo dinamico, por otra parte, se realizan usando un numero unico que indica el metodo, el cual se guarda en una clase solo si la clase lo define o sobrescribe. La busqueda de la funcion correspondiente es, por lo general, mas lenta que la busqueda de 10s metodos virtuales en la tabla, que consta de un solo paso. La ventaja es que las entradas del metodo dinamico solo se propagan a descendientes cuando estos sobrescriben el metodo. Manejadores de mensajes Tambien se puede usar un metodo de enlace posterior para manejar un mensaje de Windows, aunque la tecnica es algo distinta. Con este proposito, Delphi ofrece otra directiva, message, para definir 10s metodos de control de 10s mensajes, que habran de ser procedimientos con un unico parametro var. La directiva message va seguida del numero del mensaje de Windows que el metodo quiere controlar. ADVERTENCIA;- La direct in i e s sage tambien ,est& dispodble ,en Kylix y el bguaja y lsr RTL la soportan por cornpleto. 8ir1 embargo, par& visual del mars de hbajo de la apiicacion-CLX nd WJQY m b t o b del mensaje para enviar las natificacioneg a Basqontrolm. Por esq ra26a, impre que sea posible, se deberia usar un mitach virtual: propomionado por la biblioteca en lugar de manejar un m@hscLje de Winhws dlrectamente. Por supuesto, esto importa s61o si queremoa qiie el &go se pueda tramportar. Por ejemplo, la siguiente porcion de codigo permite manejar un mensaje defi- nido por el usuario, con el valor numirico indicado por la constante vm - User de Windows. type TForml = class (TForm) ... procedure WMUser (var Msg: TMessage) ; message vm-User; end ; El nombre del procedimiento y el tip0 de 10s parametros dependen del progra- mador, aunque esisten varios tipos de registros predefinidos para 10s diversos mensajes de Windows. Podria generarse mas adelante este mensaje, invocando a1 metodo correspondiente, como en: PostMessage (Form1 .Handle, vm-User, 0, 0) ; Esta tecnica puede resultar extremadamente util para un programador vetera- no de Windows, que lo sepa todo sobre 10s mensajes y las funciones de la API de Windows. Tambien se puede enviar inmediatamente un mensaje mediante la lla- mada a la API de SendMessage o a1 metodo Perform de la VCL. Metodos abstractos La palabra clave abstract se usa para declarar metodos que se van a defi- nir solo en subclases de la clase actual. La directiva abstract define por com- pleto el metodo, no es una declaracion que se completara mas adelante. Si se intenta definir el metodo, el compilador protestara. En Delphi se pueden crear instancias de clases que tengan metodos abstractos. Sin embargo, a1 intentarlo, el compilador de 32 bits de Delphi emite un mensaje de advertencia "Constrtrcting instance of <class name> containing abstract methods" (Creando caso de +om- bre de clase> que contiene metodos abstractos). Si se llama a un metodo abstract0 en tiempo de ejecucion, Delphi creara una escepcion, como muestra el ejemplo AbstractAnimals (una ampliacion del ejemplo PolyAnimals), que usa la siguiente clase: type TAnimal = c lass public function Voice: s t r i n g ; virtual; abstract; NOTA: La rnayoria de 10s lenguajes orientados a objetos usan un enfo m h estricto: generalmente no se pueden crear instancias de cIases que c tengan m6todos abstractos. Podriamos preguntarnos por la razon del uso de 10s metodos abstractos. Esta razon es el polimorfismo. Si la clase TAnimal tiene el metodo virtual Voice, toda clase heredada puede volver a definirlo. Si se trata de un metodo abstracto Voice, cada clase heredada debe volver a definirlo. En las primeras versiones de Delphi, si un metodo sobrescribia un metodo abstracto llamado inherited, el resultado era una llamada a1 metodo abstrac- to. A partir de Delphi 6; el compilador se ha mejorado para detectar la presencia dcl metodo abstracto y evitar la llamada inherited. Esto significa que se puede usar con seguridad inherited en todo metodo sobrescrito, a no ser que se desee inhabilitar esplicitamente la ejecucion de parte del codigo de la clase basica. Conversion descendiente con seguridad de tipos La norma sobre compatibilidad de tipos de Delphi para las clases descendien- tes permite usar una clase descendiente donde se espera una clase ascendiente. Sin embargo, el caso contrario nunca es posible. Ahora supongarnos que la clase TDog posee un metodo Eat, que no esta presente en la clase TAnimal. Si la variable MyAnimal se refiere a un perro, se podra llamar a la funcion. Pero si lo intenta y la variable se refiere a otra clase, el resultado le dara un error. A1 realizar una conversion de tipos explicita, podemos originar un fastidioso error en tiempo de ejecucion (o peor, un problema de sobrescritura de la memoria subya- cente), porque el compilador no puede establecer si el tip0 del objeto es correct0 ni si 10s metodos a 10s que se llama existen realmente. Para solucionar el proble- ma, podemos usar tecnicas basadas en informacion de tip0 en tiempo de ejecucion (abreviado RTTI). Basicamente, dado que cada objeto "conoce" su tip0 y su clase padre y podemos pedir informacion con el operador is o utilizar el metodo InheritsFrom de la clase TObject. Los parametros del operador is son un objeto y un tipo de clase y el valor de retorno es un booleano: if MyAnimal is TDog then . . . La expresion is evalua como True si se el objeto MyAnimal se refiere realmente a un objeto de clase T D O ~ o de un tipo descendiente de T D O ~ . Esto significa que si se comprueba si un objeto TDog es de tipo TAnimal, la compro- bacion tendra exito. En otras palabras, esta sentencia evalua como True si se puede asignar con seguridad el objeto (MyAnimal) a una variable del tipo de datos (TDO~). Ahora que sabemos con seguridad que el animal es un perro (dog), se puede realizar una conversion de tipos segura. Se puede realizar dicha conversion direc- ta escribiendo el siguiente codigo: var MyDog: TDog; begin if MyAnimal is TDog then begin MyDog : = TDog (MyAnimal) ; Text := MyDog.Eat; end; Esta misma operacion se puede realizar directarnente mediante el segundo ope- rador RTTI, as, que convierte el objeto solo si la clase solicitada es compatible con la real. Los parametros del operador as son un objeto y un tip0 de clase, y el resultado es un objeto convertido a1 nuevo tipo de clase. Podemos escribir el siguiente fragment0 de codigo: MyDog := MyAnimal as TDog; Text := MyDog. Eat; Si solo queremos llamar a la funcion E a t , tambien podemos usar una notacion incluso mas corta: (MyAnimal as TDog) .Eat; El resultado de esta expresion es un objeto del tip0 de datos de clase TDog, por lo que se le puede aplicar cualquier metodo de dicha clase. La diferencia entre la conversion tradicional y el uso de as es que el segundo enfoque crea una excepcion si el tip0 del objeto es incompatible con el tipo a1 que estamos intentan- do convertirlo. La excepcion creada es E I nva 1 idCa s t . Para evitar esta excepcion, hay que usar el operador is y, si funciona, realizar una conversion de tipos normal (en realidad, no hay ninguna razon para usar is y as de manera secuencial y hacer la verificacion de tipos dos veces): if MyAnirnal is TDog then TDog (MyAnimal) . Eat ; Ambos operadores RTTI resultan muy utiles en Delphi para escribir codigo generico que se pueda usar con diversos componentes del mismo tipo o incluso de distintos tipos. Cuando un componente se pasa como parametro a un metodo de respuesta a un evento, se usa un tipo de datos generico (TOb j ect), por lo que normalmente es necesario convertirlo de nuevo a1 tip0 de componente original: procedure TForml.ButtonlClick(Sender: TObject); begin if Sender is TButton then . . .end; Se trata de una tecnica habitual en Delphi. Los dos operadores RTTI, i s y as, son realmente potentes y podriamos sentirnos tentados a considerarlos como construcciones de programacion estandar. Sin embargo, probablemente se debe- ria limitar su uso para casos especiales. Cuando sea necesario resolver un proble- ma complejo relacionado con diversas clases, hay que intentar utilizar primer0 el polimorfismo. Solo en casos especiales, en 10s que el polimorfismo solo no se pueda aplicar, deberiamos intentar usar 10s operadores RTTI para complementar- lo. No hay que usar RTTI en lugar del polimorfismo, puesto que daria lugar a programas mas lentos. La RTTI, de hecho, tiene un impact0 negativo en el rendi- miento, porque debe pasar por la jerarquia de clases para ver si la conversion dc tipos es correcta. Como hemos visto, las llamadas de metodo virtual solo necesi- tan una busqueda en memoria, lo cual es mucho mas rapido. I NOTA: En realidad hay m i s informaci6n de tipo en tiempo de ejecuei6o (RTTI) que 10s operadores is y as. Se puede acceder a clases detalladas e information de tipos en tiempo de ejecucion, sobre todo para propiedades eventos y mCtodos pub1 i s hed. Uso de interfaces Cuando se define una clase abstracta para representar la clase basica de una jerarquia, se puede llegar a un punto en el que la clase abstracta sea tan abstracta que so10 liste una serie de funciones virtuales, sin proporcionar ningtin tip0 de implernentacion real. Este tip0 de clase puramente abstracta puede definirse tam- bien mediante una tecnica concreta, una interfaz. Por esta razon, nos referimos a dichas clases como interfaces. Tecnicamente, una interfaz no es una clase, aunque puede parecerlo, porque se considera un elemento totalmente a parte con caracteristicas distintivas: Los objetos de tipo interfaz dependen de un recuento de referencias y se destruyen automaticamente cuando no hay mas referencias al objeto. Este mecanismo es similar a la forma en que Delphi maneja cadenas largas y administra la memoria casi de forma automatica. Una clase puede heredar de una clase basica simple, per0 puede implementar varias interfaces. A1 igual que todas las clases descienden de T O b j ect, todas las interfaces descienden de 1 Interface y forman una jerarquia totalmente indepen- diente. m z E d i a s e r IUnknown has@ ~ e l ~ h i 5, per0 ~ e l ~ h i 7 6 le otorgo un nuevo nombre, I I n t er f ace, para paarcar de un modo rnk claro el hecho de que de esta func ih del lenguaje es independiente del COM de Microsoft (que usa IUnknown como su iaterfaz base). De hecho, las interfaces Delphi tambikn e s t h disponibles en Kylix. Es importante fijarse en que las interfaces soportan un modelo de programa- cion orientada a objetos ligeramente distinto a1 que soportan las clases. Las interfaces ofrecen una implernentacion del polimorfismo menos restringida. El polimorfismo de las referencias de objetos se basa en una rama especifica de una jerarquia. El polimorfismo de interfaces funciona en toda una jerarquia. Ademas, el modelo basado en interfaces es bastante potente. Las interfaces favorecen el encapsulado y proporcionan una conexion mas flexible entre las clases que la herencia. Hay que resaltar que 10s lenguajes orientados a objetos mas recientes, de Java a C#, poseen el concept0 de interfaces. Veamos la sintaxis de la declara- cion de una interfaz (que, por convencion, comienza con el caracter I): type ICanFly = interface ['{EAD9C4B4-ElC5-4CF4-9FAO-3B812C880A21]'] function Fly: string; end; La interfaz anterior posee un GUID, un identificador numeric0 que sigue a su declaracion y se basa en las convenciones Windows. Estos identificadores (llama- dos generalmente GUID) se pueden generar pulsando la combinacion de teclas Control-Mayus-G en el editor de Delphi. - - - - - - - . - - - - - - - - - - - - - NClTkt Awqne se $udden coMilar y usar interfaces sin:especificar un 4&RD para ellas For la general conviene generar uno, puesto que es nece- sari0 t a r s realizar consbltas & interfaz o la conversibn dinamica de tipos mediante as c y ese tipo de interfaz. Dado que todo el inter& de las interfaces coslsiste'(nomalmentefin aprovechar la flexibilidad mejorada en tiernpo I& ejecuci'6ir,'si Ta compaqmos cob 10s tipos de clase, las interfaces sin 10s GUIH no resultan muy utiles. Cuando hayamos declarado una interfaz, se puede definir una clase que la implemente, como en: type TAirplane = class (TInterfacedObj ect, ICanFly) function Fly: string; end; La RTL ya ofrece unas cuantas clases basicas para implementar el comporta- miento fundamental que necesita la interfaz I I n t e r f ace. Para 10s objetos in- ternos, se usa la clase T I n t e r f acedOb j ect , utilizada en el codigo anterior. Se pueden implementar mktodos de interfaz con metodos estiticos (como en el codigo anterior) o con metodos virtuales. Se pueden sobrescribir mktodos virtuales en subclases utilizando la directiva overr ide . Si no se usan metodos virtuales, aun asi se puede ofrecer una nueva implementacion en la subclase, volviendo a declarar el tipo de interfaz en la subclase y a enlazar 10s metodos de interfaz con nuevas versiones de 10s metodos estaticos. A primera vista, el uso de metodos virtuales para implementar interfaces parece permitir un codigo mas limpio en las subclases, per0 ambos enfoques son igual de potentes y flexibles. Sin embargo, el uso de metodos virtuales afecta a1 tamaiio del codigo y de la memoria necesaria. I NQTA: coolpiladm ha de garerar mtinas de d e w p ~ r q ajustar lm puntos b entrada & la llatnada dq infe&gaI r n b cmespgndiente de hi olase de impletneq@ci&y adaptar el punter0 self ~ s t c t-$o de mi- & m6todo de interfaz para m w a p ~ 4 t i c o s es muy sencillo: a j u m r ' s s i f y p&r al &&#o real de la clase. 'tas mtinas de mCtodo de interfaz para m&'&s virtuales son mucho mas complejas y requieren unas cuatro veces has' codigtj (20 a 30 bytes) en cada una que en el caso esthtico. Ademas, aiiadir mas- metodos virtuales a la clase de implementacion contribuye a inflat la tabla.de rn6todos virtuaIes (VMT) en la clase y en todas sus subdases. Una interfaz ya dispone de su propia VMT y volver a declarar una interfaz en las subclases para volver a enlazar la interfaz con 10s nue- vos metodos supone tanto polimorfismo como usar metodos virtuales, pet0 requiere un codigo menor, Ahora que hemos definido una implementacion de las interfaces, podemos es- cribir algo de codigo para usar un objeto de esa clase, mediante una variable de tipo interfaz: var Flyerl: ICanFly; begin Flyerl := TAirplane.Create; Flyerl.Fly; end; En el momento en que se asigna un objeto a una variable de tipo interfaz, Delphi comprueba automaticamente si el objeto implementa esa interfaz, median- te el operador as. Se puedc espresar csplicitamente esta operacion de este modo: Flyerl := TAirplane-Create as ICanFly; -- NOTA: El cornpilador genera diferente cbdigo para el operador as cuamlo se usa con intefices que cuando se usa con clases. Con clases, introduce verificaciones en tiempo de ejecuci6n para cornprobar que el objeto es efec- tivamente "compatible en tipo" con la clase dada. Con las interfaces, com- prueba en tiempo de compilaci6n que puede extraer la interfaz necesaria del tipo de clase disponible y asi lo hace. Esta operacih es como un "as en tiempo de compilation", no algo que exista en tiempo de ejecucibn. Usemos la asignacion directa o bien la sentencia as, Delphi realiza una accion extra: llama a1 metodo AddRef del objeto (definido por I Interf ace). La implementacion estand& de este mktodo, como la que ofrece TI nt er f a - cedOb j ect, es aumentar el recuento de referencias. Al mismo tiempo, desde el momento en que la variable Flyerl esta fuera de alcance, Delphi llama al meto- do Release (de nuevo parte de IInterface). La irnplementacion de ~1;ter f aced~bj ect de - Release decrementa el recuento de referencias, verifica si este es cero y, si es necesario, destruye el objeto. Por esa razon en el ejemplo anterior, no hay codigo para liberar el objeto que hemos creado. ADVERTENCIA: Cuando se usan &j&~ b& en intediuxs, por lo general deberiamos acceder a ellos s6io w n las variables da objeto o sblo con las variables de interfaz. Si se m&+zlsa hs dw ~~, el s h a de recuenta de referencias de Del* se interrump y pue&.ariginar errores de memoria que sean extre-entc diff ciles de 1-ar: J3i la piktim, si hemos decidido usar interfa~eer . probabkmeot~ deberiamosi usa r f i w e n - te variables basadas en inte&e$.' $i &d asi debcanps m e z w las vdrh- bles, lo msls aconsejable es inhev6ilitar d reopanto de-re-fer&as-esd&do m a clase base propia en lugar de usar T 1 n t er f aced~b j d ct . Trabajar con excepciones Otra caracteristica clave de Delphi es el soporte de excepciones. Las excepcio- nes hacen que 10s programas Sean mas robustos ya que proporcionan un mod0 estandar de notificar y gestionar errores y situaciones inesperadas. Las excepcio- nes hacen que 10s programas Sean mas faciles de escribir, leer y depurar porque permiten separar el codigo de gestion de errores de codigo normal, en lugar de entremezclar ambos. A1 obligar a mantener una division logica entre el codigo y la gestion de errores y a1 conmutar al manejador de errores automaticamente, se consigue que la logica real resulte mas limpia y clara. Nos permiten escribir un codigo mas compacto y menos inundado por 10s habituales metodos de manteni- miento no relacionados con el objetivo real de programacion. En tiempo de ejecu- cion, las bibliotecas de Delphi crean excepciones cuando algo va ma1 (en el codigo de tiempo de ejecucion, en un componente, en el sistema operativo). Desde el punto del codigo en el que se crea, la escepcion se pasa a su codigo de llamada, y asi sucesivamente. Por ultimo, si ninguna parte del codigo controla la excepcion, la VCL se encarga de ella, mostrando un mensaje estandar de error y tratando de continuar el programa proporcionando el siguiente mensaje del sistema o peticion a1 usuario. Todo este mecanismo se basa en cuatro palabras clave: try: Delimita el comienzo de un bloque protegido de codigo. except: Delimita el final de un bloque protegido de codigo e introduce las sentencias de control de excepciones. finally: Se usa para especificar bloques de codigo que han de ejecutarse siempre, incluso cuando se dan excepciones. Este bloque se usa general- mente para realizar operaciones de limpieza que siempre se deberian ejecu- tar, como cerrar archivos o tablas de bases de datos, liberar objetos y liberar memoria y otros recursos adquiridos en el mismo bloque de progra- ma. raise: Es la sentencia usada para generar la excepcion. La mayoria de las excepciones que encontramos en programacion en Delphi las genera el sistema, per0 tambien'se pueden crear excepciones propias en el codigo, cuando se descubren datos no validos o incoherentes en tiempo de ejecu- cion. La palabra clave r a i s e tambien puede usarse dentro de un contro- lador para volver a crear una excepcion, es decir, para propagarla a1 siguiente controlador TRUCO: La gestibn de excepciones no supone un reemplazo aI adecuado control de flujo en un progami. Es rcmmendabfe mmtener el uso de sen- *._ _ - _ . C _ n _ I d 3 . I L- , I 1- >.:_ rencias para co~nproopr m cqmaa aei usuano y ouas posmres concucio- nes de error. S61o d&erf& asarse ex.cepcianes para eituaciones a n o d e s o inesperadas . Flujo de programa y el bloque finally La potencia de las excepciones en Delphi tiene que ver con el hecho de que se "pasan" de una rutina o metodo del llamador, hasta un manejador global (si el programa ofrece uno, como suele suceder con las aplicaciones de Delphi), en lugar de seguir la ruta estandar de ejecucion del programa. Asi que el autentico problema no consiste en saber como detener una excepcion sin0 como ejecutar codigo incluso aunque se lance una excepcion. Consideremos esta seccion de codigo (parte del ejemplo TryFinally), que rea- liza algunas operaciones para las que emplea bastante tiempo y usa el cursor en forma de reloj de arena para mostrar a1 usuario que esta haciendo algo: Screen.Cursor := crHourglass; // gran a l g o r i t m o . . . Screen.Cursor : = crDefault; En caso de que se produzca un error en el algoritmo (corno el que se ha inclui- do a proposito en el ejemplo TryFinally), el programa se detendra, per0 no volve- ra a establecer el cursor predefinido. Es para esto para lo que sirve un bloque t r y / f i n a l l y : Screen.Cursor := crHourglass; try // gran a l g o r i tmo . . . finally Screen.Cursor : = crDefault; end ; Cuando el programa ejecuta esta funcion, siempre reinicia el cursor, haya una excepcion (de cualquier tipo) o no. Este codigo no controla la excepcion, simple- mente hace que el programa sea robusto en caso de que se Cree un una excepcion. Un bloque t r y puede ir seguido de una sentencia e x c e p t o f i n a l l y , per0 no por ambas a1 mismo tiempo. La solucion mas comun para controlar tambien la excepcion consiste en usar dos bloques t r y anidados. En ese caso, hay que asociar el interno con una sentencia f i n a 11 y y el externo con una sentencia e x c e p t o viceversa, segun lo requiera la situacion. Aqui tiene el esquema del codigo para el tercer boton del ejemplo T r y F i n a l l y : Screen.Cursor : = crHourglass; try try // gran a l g o r i tmo . . . finally Screen.Cursor : = crDefault; end; except on E: EDivByZero do . . . end; Cada vez que haya algun codigo de finalizacion a1 concluir un metodo, hay que situar dicho codigo en un bloque f i n a l l y . Siempre se deberia, invariablemente y de forma continuada proteger el codigo con sentencias f i n a l l y , para evitar problemas de recursos o de goteos de memoria en caso de que se Cree una excep- cion. - . - - - - - - - - - --- - - - - - - - TRUCO: Controlar la excepcion es generalmente mucho menos importan- te que utilizar 10s bloques f i n a l l y , puesto que Delphi puede sobrevivir a la mayoria de ellas. Ademas, dernasiados bloques para controlar excepcio- r nes en el c6digo probablernente indicarh errores en el flujo del programa y una mala comprension de la funcion de las excepciones en el lenguaje. .-.. . . . . . ... . .. . - . - - mtre 10s ejemplos ae este llbr0 ser veran mucaos bloques t r y / r l n a l l y , unas cuantas sentencias raise, y casi ningin bloque t r y / e x c e p t . Clases de excepciones En las sentencias de control de escepciones mostradas anteriormente, capta- mos la excepcion EDivBy Zero, que define el RTL de Delphi. Otras excepcio- nes como esta se refieren a problemas en tiempo de ejecucion (como una conversion dinamica erronea), problemas de recursos de Windows (como 10s errores por falta de memoria), o errores de componentes (como un indexado erroneo). Los progra- madores pueden definir tambien sus propias excepciones. Se puede crear una nueva subclase de escepciones predefinidas o de una de sus subclases: type EArrayFull = c lass (Exception) ; Cuando se aiiade un nuevo elemento a una matriz que ya esta llena (probable- mente por un error en la Iogica del programa), se puede establecer la excepcion correspondiente, creando un objeto de esa clase: if MyArray.Ful1 then ra ise EArrayFull .Create ( 'Ma t r i z l l e n a ' ) ; Este metodo c r e a t e (heredado de la clase E x c e p t i o n ) tiene un parametro de cadena para describir la excepcion a1 usuario. No es necesario preocuparse de destruir el objeto creado para la excepcion, porque se borrara automaticamente gracias a1 mecanismo de control de excepciones. El codigo presentado en 10s extractos anteriores forma parte de un programa ejemplo, denominado Exception 1. Algunas de las rutinas se han modificado lige-ramente, como en la siguiente funcion Div ideTwiceP lusOne : function DivideTwicePlusOne (A, B: Integer) : Integer; begin try // e r r o r s i B e s i g u a l a 0 Result : = A div B; // h a c e o t r a c o s a . . . o b v i a r s i s e c r e a una e x c e p c i o n Result : = Result div B; Result := Result + 1; except on EDivByZero do begin Result := 0; MessageDlg ('Dividir por cero corregido.', mtError, [ m b O K l , 0); end ; on E : Exception do begin Result := 0 ; MessageDlg (E.Message, mtError, [mbOK] , 0) ; end; end; // finaliza except end; En el codigo de Esceptionl hay dos excepciones diferentes despues del mismo bloque t r y . Puede haber un numero cualquiera de controladores de este tipo, evaluados consecutivamente. Si se usa una jerarquia de excepciones, tambien se llama a un controlador para las subclases del tip0 a las que se refiere, como haria cualquier procedimiento. Por csta razon es necesario colocar 10s controladores de excepciones de mayor ambito (10s de la clase E x c e p t i o n ) a1 final. Pero hay que tener presente que utilizar un controlador para cada excepcion, como el anterior, no suele ser una buena opcion. Es mejor dejar las excepciones desconocidas para Delphi. El con- trolador de excepciones por defecto de la VCL muestra el mensaje de error de la clase de escepcion en un cuadro de mensaje y, a continuacion, reanuda el funcio- namiento normal del programa. En realidad se puede modificar el controlador de escepciones normales con el evento A p p l i c a t i o n . OnExcept ion o el even- to OnExcep t ion del componente A p p l i c a t i o n E v e n t s , como se demues- tra en el ejemplo ErrorLog posterior. Otro importante elemento mas del codigo anterior es el uso del objeto de excepcion en el controlador (vease en E: E x c e p t i o n do). La referencia E de la clase E x c e p t i o n se refiere a1 objeto de excepcion pasado por la sentencia r a i s e . Cuando se trabaja con escepcio- nes, hay que recordar esta norma: se lanza una excepcion mediante la creacion de un objeto y se controla mediante la indicacion de su tipo. Esto nos ofrece una importante ventaja, porque como hemos visto, cuando se controla un tipo de excepcion, en realidad se controlan excepciones del tip0 que se especifica asi como de cualquier otro tip0 descendiente. A1 arrancar un programa en el entorno Delphi (por ejemplo, a1 pulsar la tecla F9), por lo general se ejecuta en el depurador. Cuanda se encuentra una excepcion, el depurador detendrb por defecto el programa. Asi, sabre- mos donde tuvo lugar la excepcion y podremo~ ver la llamada del controla- dor paso a paso. Tambih se puede usar la cafacteristica Stack Tmce de Delphi para ver la secuencia de la funci6n y las llamadas de d o d o que dieron lugar a que el programa crease una ex&pci6n. portamiento confundira a un programador que no sepa bien c6mo funciona el depurador de Delphi. Aunque se prepare el dd igo para controlar de f o m a adecuada la excepcih, el depurador detendni la ejecucib del pro- grama en la linea de c6digo fuente m& cercana a1 lvgar m ~l qw se cfeb la excepcion. Asi, a1 moverse paso a paso por el cMgo, puede verse se controla. Si sirnplemente queremos dejar que el program se ejtcutt mamb la excepci6n se controla correctamente, hay que ejecutar el pr0gms.m des& el Explorador de Windows o desactivar temporalme& la deteaqhh $top en las opcioncs de Delphi Extxptions de la ficha ~&guage b c d p f h s del cuadro de diirlogo Debugger Options (activada mediaate la wden Tools> Debugger Options), que aparece en la ficha Language Exceptions del cuadro de diilogo Debugger Options que se muestra a continuation. Tambien se puede detener el depurador. Sin X r G , e x caso del progrim & ejemp1o dxceptioni este cirn---~ 'VCL EAbort Exceptions lndy EIDConnCbsedG~acelul?y Enceplm ' Mtc~osoR DAD Excepl~ons ' V~s~Broke~ lntelnal Except~ons 't CORBA Syslem Exceplans CORBA User Excepl~onr Registro de errores La mayor parte del tiempo, no se sabe que operacion va a crear una excepcion y no se puede (ni se debe) envolver cada una de las partes del codigo en un bloque try/except. La tecnica general consiste en dejar que Delphi controle todas las escepciones y finalmente pasarselas todas a1 usuario, mediante el control del evento OnException del objeto global Application. Esto se puede hacer de un mod0 mas sencillo con el componente ApplicationEvents. En el ejemplo ErrorLog, se ha aiiadido a1 formulario principal una copia del componen- te Appl icat ionEvent s y un controlador para su evento OnExcept ion: procedure TForrnLog. LogException (Sender: TObj ect; E: Exception) ; var Filename: string; LogFile : TextFile; begin // prepara un archivo de r e g i s t r o Filename := ChangeFileExt (Application.Exename, ' . l o g 1 ) ; AssignFile (LogFile, Filename) ; if FileExists (FileName) then Append (LogFile) // abre un archivo e x i s t e n t e e l s e Rewrite (LogFile); // crear uno nuevo t r~ // e s c r i b e en un archivo y mostrar e r r o r Writeln (LogFile, DateTimeToStr (Now) + ' : ' + E-Message) ; if not CheckBoxSi1ent.Checked then Application. ShowException (E) ; finally // c i e r ra e l archivo CloseFile (LogFile) ; end: - - -- - - - - - . . . - - . . . - - . . - - . - . . - . - . . - - -- ~~ - - - - - - - . - NOTA: El ejemplo Er ro rhg usa el soporte de archivos de texto que pro- porciona el tradicional t i p de datos Turbo Pascal TextFile. Se puede asig- nar una variable de archivo de texto a un archivo real y despues leerlo o escribirlo. En el controlador de excepciones global, se puede escribir en el registro, por ejemplo, la fecha y hora del evento y tambien decidir si mostrar la excepcion como suele hacer Delphi (ejecutando el metodo ShowException de la clase TApplicat ion). De hecho, Delphi ejecuta ShowExcept ion de manera pre- determinada solo si no hay instalado un controlador OnException. La figura 2.8 muestra el programa ErrorLog en ejecucion y una excepcion de muestra abierta en ConTEXT (una practico editor para programadores incluido con Delphi y disponible en w~vw.fixedsys.com/context). Referencias de clase La ultima caracteristica del lenguaje que trataremos en este capitulo son las referencias de clase, lo cual implica la idea de manipular las propias clases dentro del codigo. El primer punto que hemos de tener en cuenta es que la referencia de clase no es un objeto; es sencillamente una referencia a un tipo de clase. Un tipo de referencia de clase establece el tip0 de una variable de referencia de clase. Aunque esto suene confuso, con unas cuantas lineas de codigo quedara un poco mas claro. . - .- - . - 119 . . . . . . . . . . . . . . ...................... 1 17/05/2003 ll:37:48:Divisiun bv zero - -- I 1 17/05/2003 ll:37:53: raise button pressed I Div by 0 1 ........ ....... 7/05/2003 11:37:56:Divislon by zero 7/05/2003 ll:37:58:raise button pressed 7/05/2003 ll:37:59:raise button pressed 7/05/2003 11:38:00:raise button pressed Figura 2.8. El ejemplo ErrorLog y el registro que produce. Supongamos que hemos definido la clase T M y C l a s s . Ahora, se puede definir un nucvo tipo de referencia de clase relacionado con dicha clase: type TMyClassRef = class of TMyClass; Ahora se pueden declarar variables de ambos tipos. La primera variable se reficre a un objeto, la segunda a una clase: var AnOb j ect : TMyClass; AClassRef: TMyClassRef; begin AnObject : = TMyClass.Create; AClassRef : = TMyClass; Podriamos preguntarnos para que se usan las referencias de clase. En general, las referencias de clase permiten manipular un tipo de datos de clase en tiempo de ejecucion. Se puede usar una referencia de clase en cualquier espresion en la que sea valido el uso de un tipo de datos. En realidad, no hay muchas expresiones de este tipo, per0 10s pocos casos que esisten son interesantes, comola creacion de un objeto. Podemos rescribir las dos lineas anteriores del siguiente modo: AnObject : = AC1assRef.Create; Esta vez hemos aplicado el constructor create a la referencia de clase en lugar de a una clase real. Hemos utilizado una referencia de clase para crear un objeto de dicha clase. Los tipos de referencia de clase no serian tan utiles si no soportasen la misma norma de compatibilidad de tipos que se aplica a 10s tipos de clase. Cuando se declara una variable de referencia de clase, como MyClas s Ref, se le puede asignar esa clase especifica y cualquier subclase. Por lo tanto, si TMyNewClas s es una subclase de nuestra clase, tambien se puede escribir AClassRef := TMyNewClass; "uno" Delphi declara una larga lista de referencias de clase en la biblioteca de tiempo de ejecucion y en la VCL, como por ejemplo las siguientes: TClass = class of TObject; TComponentClass = class of TComponent; TFormClass = class of TForm; En concreto, el tipo de referencia de clase TC la s s se puede usar para almace- nar una referencia de cualquier clase que se escriba en Delphi, porque toda clase se deriva en ultimo termino de TOb j ec t . La referencia T FormClas s, en cam- bio, se usa en el codigo fuente de la mayoria de 10s proyectos Delphi. El metodo Create Form del objeto Appl i cat ion, en realidad, requiere como parametro la clase del formulario que va a crear: Application. CreateForm(TForm1, Forml) ; El primer parametro es una referencia de clase, el segundo es una variable que almacena una referencia a la instancia de objeto creada. Por ultimo, cuando se tiene una referencia de clase, se le pueden aplicar 10s metodos de clase de la clase relacionada. Si tenemos en cuenta que cada clase hereda de TOb j ec t, se pueden aplicar a cada referencia de clase algunos de 10s metodos de TObject. Crear componentes usando referencias de clase El uso practico de las referencias de clase en Delphi consiste en poder manipu- lar un tip0 de datos en tiempo de ejecucion, lo cual es un elemento fundamental del entorno Delphi. Cuando se aiiade un componente nuevo a un formulario, se- leccionandolo de la Component Palette, se selecciona un tip0 de datos y se crea un objeto de dicho tipo de datos. (En realidad, eso es lo que Delphi hace sin que podamos verlo.) En otras palabras, las referencias de clase aportan polimorfismo para la construction de objetos. Para que pueda hacerse una idea de como funcio- nan las referencias de clase, hemos creado un ejemplo llamado ClassRef. El for- mulario que aparece en este ejemplo es bastante sencillo. Tiene tres botones de radio, situados dentro de un panel en la parte superior del formulario. Cuando seleccionamos uno de estos botones de radio y hacemos clic sobre el formulario, podremos crear nuevos componentes de estos tres tipos indicados por las etique- tas del boton: botones de radio, botones de pulsador y cuadros de edicion. Para que este programa se ejecute correctamente, es necesario modificar 10s nombres de 10s tres componentes. El formulario tambien tendra un campo de referencia de clase, declarado como C l a s s R e f : T C o n t rolclass. Almace- na un nuevo tipo de datos cada vez que el usuario hace clic sobre uno de 10s tres botones de radio, con asignaciones como C l a s s R e f : = T E d i t . La parte interesante del codigo se ejecuta cuando el usuario hace clic sobre el formulario. Hemos escogido de nuevo el evento OnMouseDown del formulario para tener acceso a la posicion del cursor del raton: procedure TForml.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer) ; var NewCtrl: TControl; MyName: String; begin / / c r e a e l c o n t r o l NewCtrl := ClassRef-Create (Self); / / l o o c u l t a t e m p o r a l m e n t e , para e v i t a r e l p a r p a d e o NewCtrl.Visible : = False; / / d e f i n e p a d r e y p o s i c i d n NewCtrl . Parent : = Self; NewCtrl-Left := X; NewCtrl.Top : = Y; / / c a l c u l a e l nombre u n i c o ( y e l t i t u l o ) Inc (Counter) ; MyName := ClassRef .ClassName + IntToStr (Counter) ; Delete (MyName, 1, 1); NewCtrl.Name : = MyName; / / l o r n u e s t r a a h o r a NewCtrl.Visible := True; end ; La primera linea del codigo de este metodo es la clave. Crea un nuevo objeto del tipo de datos de clase almacenados en el campo C l a s s R e f . Esto se consigue simplemente aplicando el constructor create a la referencia de clase. Ahora se puede establecer el valor de la propiedad P a r e n t , fijar la posicion del nuevo componente, darle un nombre (que se usa tambien automaticamente como C a p t i o n o T e x t ) y hacerlo visible. I NOTA: Para que fkncione la construccion polimorfica. el tipo de la clase I basica de la referencia de clase habd dc tener un constructor virtual. Si re maw constructor virtual (corno en el ejemplo), la llamada dcl constructor apljcgda la referencia de clase llamara at constructor del tip0 al que realmente sc refiere la variable de referencia de clase. Pero sin un construc- tax virtual, el cbrfigQ llamara a1 constructor del tipo dc clase fijo indicado exi k~&clwa&:de Ia referencia de clase. Los constructores son nccesarios para la construccion,~olimorf~ca del mismo modo que 10s mktodos virtuales son nccesariba para ei poIimorfismo. biblioteca en tiempo El lenguaje de programacion Delphi favorece un enfoque orientado a objetos, junto con un estilo visual de desarrollo. Es aqui donde sobresale Delphi y tratare- mos acerca del desarrollo visual y basado en componentes a lo largo de este libro; sin embargo, deseo subrayar el hecho de que muchas de las caracteristicas listas para ser utilizadas de Delphi proceden de su biblioteca en tiempo de ejecucion (RTL). Se trata de un gran conjunto de funciones que puede utilizar para realizar tareas sencillas, a1 igual que algunas complejas, dentro de su propio codigo Pascal. (Utilizo aqui "Pascal" porque la biblioteca en tiempo de ejecucion contiene princi- palmente procedimientos y funciones escritas con 10s mecanismos tradicionales del lenguaje y no con las extensiones de orientacion a objetos aiiadidas al lenguaje por Borland.) Existe un segundo motivo para dedicar este capitulo del libro a la biblioteca en tiempo de ejecucion: Delphi 6 supuso un gran numero de mejoras en este campo, y Delphi 7 aporta algunas mejoras mas. Estan disponibles nuevos grupos de fun- ciones, se han desplazado funciones a nuevas unidades y han cambiado otros elementos, lo que crea unas pocas incompatibilidades con el codigo antiguo a partir del cual podria adaptar sus proyectos. Por eso, incluso aunque haya utiliza- do las versiones antiguas de Delphi y se sienta comodo con la RTL, aun asi deberia leer a1 menos parte de este capitulo. Este capitulo comenta 10s siguientes temas: Nociones generales de la RTL. Funciones de la RTL de Delphi. El motor de conversion Fechas, cadenas de caracteres y otras nuevas unidades de la RTL. Informacion de clase en tiempo de ejecucion. Las unidades de la RTL En las versiones mas recientes de Delphi, la RTL (biblioteca en tiempo de ejecucion) posee una nueva estructura y varias unidades nuevas. Borland aiiadio nuevas unidades ya que tambien aiiadio numerosas funciones nuevas. En la mayo- ria de 10s casos, las funciones existentes se encuentran en las unidades en las que solian estar, pero las nuevas funciones aparecen ahora en unidades especificas. Por ejemplo, las nuevas funciones relacionadas con fechas estan en la unidad DatcUtils, per0 las funciones de fecha que ya existian no se han movido de SysUtils, para evitar incompatibilidades con el codigo existente. La excepcion a esta norma tiene que ver con algunas de las funciones de soporte de variantes, que se han extraido de la unidad System para evitar enlaces no deseados de bibliotecas especificas de Windows, incluso en programas que no utilizaban dichas caracteristicas.Estas funciones variantes son ahora parte de la nucva unidad Variants. AD D Dephi 5 necesite ntilizar esta nueva unidad Variants para volver a compi- lar. Delphi es lo suficientemente listo como para darse cuenta de ello e incluir automiticamente la unidad Variants en proyectos que usan el tipo Variant , emitiendo unicarnente UM advertencia. TambiCn se han aplicado ciertos ajustes para reducir el tamaiio m i n i m ~ de un archivo ejecutable, a veces ampliado por inclusiones no deseadas de variables globales o codigo de inicializacion. I nucmn a e ~ ramano mumno ae programa en unos cuantos ha parece algo El tamafio emutable bajo el microscopio ncnte, pero suponc una gran ayuda para 10s desarrolladoTes. En aQpgtbs casos. incfuso anos cuantos KB (rttuItiplicados pur muchas apliCaciones) puedcn reducir cl tam-iio y, cn ultima instancia. el tiempa de descarga. Como pcqueiia prueba, h a s ercado el programa Minisiic, que n o e s a n I en una cadena y no incluir sysufjls, ,que definc toc mas complejas e implicaria un pequcfia numcntb I Si este prograrna se compila con Delphi 5. s e ~ w i . - - .--. - . . . - . i. . - scr y muesua el resulraao en mymensajc. cr p g a m a nomme vauanas de alto nivel. A b m i x se utilka la funcibn s k r ~ p a r a cu&ertir un edero- 4-f @ cf: de 18.452 bytes. Uelphi b reduce dicho tamano a mh 1 5 . 3 6 ~ ~ b y t e . recor- le I- tando unos 3 KB. Con Delphi 7. qFtama5o o~.solg Iigeranl,en& mayor. c 15.872 bytes. A1 reemplazar laiWena larg&.por m a caden? colta y mod 7- --o-- ------ Y -- r-- ta menos de 10 KB. Esto es debido a r de scmarte deedenas v tamhih el se que se acabath eliminando las rutinas -- - - r ---- - , -- ,:stor de memoria, lo cud es hicamen- te posible en programas que utilizan exclusivamente llamadas de bajo ni- vel. Se pueden enwntrar ambas versiones en el c6digo fuente del archivo de ejemplo. Fijese, de todos modos, en que las decisiones de este tip^ siempre implican una sene de concesiones. A1 eliminar el encabezamiento de variantes de las aplicaciones Delphi qure no las usan, por ejemplo, Borland ha aiiadido una carga extra a ias aplicaciones que si lo ham. La ventaja real de esta opera- cibn, sin embargo, estA en el reducido tamafio en memoria que necesitan las aplicaciones Delphi que no usan variantes, como consecuencia de no tener que introducir varios megabytes debido a las bibliotecas de sistema Ole2. Lo realmente importante, en mi opini&n, es el tamaiio de las grandes aplica- ciones Delphi basadas en paquetes en tiempo de ejecuci6n. Una sencilla prueba con un programa que no hace nada, el ejemplo Minipack, muestra un ejecutable de 17.408 bytes. En 10s siguientes apartados encontrara una lista de las unidades de la RTL en Delphi, asi como de todas las unidades disponibles (con el codigo fuente comple- to) que se encuentran en el subdirectorio Source\Rtl\Sys del directorio Delphi y algunas de las disponibles en el subdirectorio Source\Rtl\Common. Este segundo directorio contiene el codigo fuente de las unidades que conforman el nuevo paquete de la RTL, que engloba tanto la biblioteca basada en funciones como las clases centrales comentadas m h adelante. Comentare de forma breve el papel de cada unidad y tambien 10s grupos de funciones incluidas. Ademas dedicare mas espacio a las unidades mas nuevas. No se trata de ofrecer una lista detallada de las funciones incluidas, ya que la ayuda electronica incluye un material de referencia similar. Sin embargo, la intencion es fijarse en algunas funciones interesantes o poco conocidas. Las unidades System y Syslnit System es la unidad principal de la RTL y se incluye automaticamente en cualquier compilacion (siempre que haya una sentencia uses automatica e im- plicita que se refiera a ella). En realidad, si intentamos aiiadir la unidad a la sentencia uses de un programa, obtendremos el siguiente error en tiempo de compilacion: [Error] Identifier redeclared: System La unidad System se compone entre otras cosas de: La clase TO^ j ect, que es la clase basica de toda clase definida en el lenguaje Pascal orientado a objetos, como todas las clases de la VCL. Las interfaces IInterf ace, IInvokable, IUnknown y IDispatch, asi como la clase de implementation simple T Int er f acedOb j ec t . I ~nterface se aiiadio en Delphi 6 para recalcar el hecho de que el tip0 de interfaz en la definicion del lenguaje Delphi, no depende en mod0 algu- no del sistema operativo Windows. I~nvokable se aiiadio en Delphi 6 para soportar las llamadas basadas en SOAP. Codigo de soporte de variantes, como las constantes de tip0 variante, el tip0 de registro TVarData y el nuevo tipo TVariantManager, un amplio numero de rutinas de conversion de variantes y tambien registros variantes y soporte de matrices dinamicas. En este ambito ha habido un monton de cambios en comparacion con Delphi 5. Muchos tipos de datos basicos, como 10s tipos de punteros y de matrices y el tipo TDateTime. Rutinas de asignacion de memoria, como GetMem y FreeMem y el pro- pio administrador de memoria, definido por el registro TMemoryManager y a1 que se accede mediante las funciones GetMemoryManager y SetMemoryManager. Para mas informacion, la funcion GetHeap- St a tus devuelve una estructura de datos THeapS t a t us. Dos nuevas variables globales (A1 1ocMemCount y A1 locMemSi ze) guardan el numero y tamaiio total de 10s bloques de memoria asignados. En el capitulo sobre la arquitectura de las aplicaciones Delphi encontrara mas informa- cion sobre la memoria y estas funciones. El codigo de soporte de modulos y paquetes, como el tip0 de punter0 PackageInf o, la funcion global GetPackageInf oTable y el pro- cedimiento EnumModules. Una lista bastante larga de las variables globales, como el caso de aplica- cion Windows MainInstance; IsLibrary, que indica si el archivo ejecutable es una biblioteca o un programa independiente; Isconsole, que indica aplicaciones de consola; I s M u l t i T h r e a d , que indica si esis- ten hilos de proceso secundarios; y la cadena de la linea de comandos CmdLine. (La unidad incluye tambien P a r a m c o u n t y ParamS t r para poder acceder mas facilmente a 10s parametros de la linea de comandos.) Algunas de estas variables son especificas de la plataforma Windows, otras estan tambien disponibles en Linux, mientras que otras son especificas de Linux. El codigo de soporte de hilos de proceso (threads), con las funciones B e g i n T h r e a d y E n d T h r e a d ; registros de soporte de archivos y ruti- nas relacionadas con archivos; rutinas de conversion de cadenas anchas y cadenas OLE; asi como muchas otras rutinas de sistema y de bajo nivel (junto con una serie de funciones de conversion automaticas). La unidad que acompaiia a System, denominada SysInit, incluye el codigo de inicializacion, con funciones que rara vez se utilizaran directamente. Esta es otra unidad que siempre se incluye de forma implicita, puesto que la unidad System hace uso de ella. Cambios recientes en la unidad System Ya se han mencionado algunas caracteristicas interesantes de la unidad System. La mayoria de 10s cambios estan relacionados con el objetivo de conseguir que la RTL de Delphi sea mas facil de transportar entre distintas plataformas, reempla- zando caracteristicas especificas de Windows por implementaciones genericas que ahora comparten Delphi y Kylix. De acuerdo con esta tendencia, existen nombres nuevos para tipos de interfaz, soporte para variantes totalmente revisa- do, nuevos tipos de punteros, soporte de matrices dinamicas y funciones para personalizar la adrninistracion de 10s objetos de excepcion. uso que se hace de la c&qdacibn condicio& con muchas referencias a ($IFDEF L M ) p ($WDEF MSTKMDOWS), que se usan para diferen- ciar entre 10s dos sistemas o~erativos. Fiiese en me aara Windows. Borland utiliza MSWINDOWS &a indicar 1; plataf&r& a1 complete, ya q eWINDOWS se utilizaba en las versiones de 16 bits del sistema operativo (en contraste con el simboIo WIN32). Por ejemplo, otro aiiadido para la compatibilidad entre Linux y Windows esta relacionado con 10s saltos de linea en 10s archivos de testo. La variable DefaultTextLineBreakStyle, afecta a1 comportamiento de las rutinas que leen y escriben en archivos, como la mayoria de las rutinas de flujos de texto. Los valores posibles para esta variable global son t l b s L F (valor predetermina- do en Kylix) y t l b s C R L F (valor predeterminado en Delphi). El estilo de salto de linea tambien se puede configurar archivo por archivo mediante la funcion S e t T e x t L i n e B r e a k S t y l e . Del mismo modo, la constante global de cadena s L i n e B r e a k tiene el valor #13#10 en la version Windows del entorno de desarrollo y el valor # 1 0 en la version para Linux. Otro cambio es que la unidad System incluye ahora las estructuras T F i leRec y TTex t Rec, que en versiones anteriores de Delphi estaban definidas dentro de la unidad S y s u t i l s . Las unidades SysUtils y SysConst La unidad SysConst define una serie de cadenas de constantes utilizadas por otras unidades RTL para mostrar mensajes. Estas cadenas se declaran con la palabra clave r e s o u r c e s t r i n g y se guardan en 10s recursos de programa. A1 igual que otros recursos, se pueden traducir mediante el Integrated Translation Manager o el External Translation Manager. La unidad SysUtils es un conjunto de utilidades del sistema de varios tipos. A diferencia de otras unidades RTL, es en gran parte una unidad dependiente del sistema operativo. La unidad SysUtils no posee un enfoque especifico, sino que engloba una pequeiia parte de todo, desde la gestion de cadenas a1 soporte de caracteres multibyte y locales, desde la clase Excep t i o n y muchas otras clases de excepcion derivadas a una multitud de constantes y rutinas de formato de cadena. Algunas de las caracteristicas de SysUtils las utilizan todos 10s progra- madores a diario, como las funciones de formato de cadena I n t T o S t r o Format. Otras caracteristicas son menos conocidas, como el caso de las variables globales de informacion sobre la version de Windows. st as indican la plataforma Windows (Window 9x o NT/2000/XP), la version del sistema operativo y el numero de creacion, asi como el paquete de servicio instalado. Se pueden usar del mismo mod0 que en el siguiente codigo, extraido del ejemplo Winversion: case Win32Platform of VER-PLATFORM-WIN32-WINDOWS: ShowMessage ('Windows 9x'); VER-PLATFORM-WIN32-NT: ShowMessage ( 'Windows NT ' ) ; end: ShowMessage ( 'Ejecutando en Windows: ' + IntToStr (Win32MajorVersion) + ' . ' + IntToStr (Win32MinorVersion) + ' (Creaci6n ' + IntToStr (Win32BuildNumber) + ' ) ' + #10#13 + ' Actualizacidn: ' + Win32CSDVersion) ; El segundo fragment0 de codigo crea un mensa-je como el que muestra en la siguiente figura, dependiendo, claro esta, de la version del sistema operativo que se hava instalado. Otra caracteristica poco conocida de esta unidad es la clase TMul t iRead- ExclusiveWriteSynchronizer (probablemente la clase VCL de nombre mas largo). Borland ha definido un alias para la clase, que es mucho mas corto: TMREWSync (ambas clases son identicas). Esta clase soporta multithreading: permite trabajar con recursos que pueden usar diversos threads a1 mismo tiempo para leer (multilectura), pero que a1 escribir han de utilizar un unico thread (es- critura exclusiva). Esto significa que no se puede comenzar a escribir hasta que todos 10s threads de lectura hayan terminado su labor. La implementation de la clase TMultiReadExclusiveWriteSyn- c h r o n i z e r se ha actualizado en Delphi 7, pero mejoras similares estan dispo- nibles en forma de un parche que aparecio tras la segunda actualizacion de Delphi 6. La nueva version de la clase esta mas optimizada y menos sujeta a bloqueos, que suelen ser un problema habitual del codigo de sincronizacion. - - NOTA: El sincronizador multilectura es linico porque soporta bloqueos recursivos y conversi6n de 10s bloqueos de lectura en bloqueos de escritura. El objetivo principal de la clase es permitir un acceso rapid0 y facil a diversos threads de lectura a1 recurso compartido, per0 a h asi pennitir que un thread obtenga el control exclusive del recurs; para reali&r actua- lizaciones relativamente poco frecuentes. Hay otras clases de sincronizacion - . - . - . . .. . . . --. .a - - . en Delphi, declaradas en la unidad SyncOb j s (disporuble baj0 Source / Rt 1 /Common) y con correspondencia directa con 10s objetos de sincronizaci6n del sistema operativo'(como eventos y secciones criticas en Windows). Nuevas funciones de SysUtils Durante las ultimas versiones, Delphi ha aiiadido algunas funciones nuevas dentro de la unidad SysUtils. Uno de 10s nuevos campos esta relacionado con la conversion de booleano a cadena. La funcion Boo lToSt r por lo general devuel- ve -1 y 0 como valores verdadero o falso. Si se especifica el segundo parametro optional, la funcion devuelve la primera cadena de las matrices T r u e BoolS t rs y F a l s e B o o l S t r s (por defecto T R U E y FALSE): BoolToStr (True) / / devuelve '-1 ' BoolToStr (False, True) / / devuelve 'FALSE' por de fec to La funcion inversa es S t r T o B o o l , que puede convertir una cadena que con- tenga uno de 10s valores de las dos matrices booleanas mencionadas anteriormen- te o un valor numerico. En este ultimo caso, el resultado sera verdadero si el valor numerico es distinto de cero. Se puede ver una sencilla demostracion del uso de las funciones de conversion booleanas en el ejemplo StrDemo. Otras funciones aiiadidas a SysUtils estan relacionadas con las conversiones de coma flotante en tipos divisa y fecha-hora: FloatToCurr y FloatToDateTime se pueden usar para evitar una conversion de tipos explicita. Las funciones TryStrTo- Float y Tr yS t rToCur r intentan convertir una cadena en un valor de coma flotante o de divisa, y, en caso de error, devuelven el valor False en lugar de generar una excepcion (corno hacen las clasicas funciones St rTo Float y StrToCurr). La Ans i Dequot edS t r, que elimina comillas de una cadena, se correspon- de con la funcion Ans iQuot est r aiiadida en Delphi 5. Con respecto a las cadenas, desde Delphi 6 existe un soporte muy mejorado de cadenas anchas, con una serie de rutinas como Wideuppercase, WideLowerCase, WideCompareStr,WideSameStr,WideCompareText,WideSameText y WideFormat. Todas estas funciones se utilizan como sus homologos AnsiString. Existen tres funciones (TryStrToDate, TryEncodeDate y Tr yEncodeTime) que intentan convertir una cadena en una fecha o codificar una fecha u hora, sin crear una excepcion, de un mod0 similar a las funciones Try antes mencionadas. Ademas, la funcion Decode Da t e Full y devuelve in- formation mas pormenorizada, como el dia de la semana y la funcion Current Y ear devuelve el aiio de la fecha actual. Hay una version que se puede transportar, sobrecargada de la funcion Get E nvironmentvar i ab le . Esta nueva version usa parametros de cadena en lugar de parametros PChar y es, en definitiva, m b facil de utilizar: function GetEnvironmentVariable(Name: string): string; Otras funciones nuevas estan relacionadas con el soporte de interfaz. Dos nuevas versiones sobrecargadas de la poco conocida funcion support permiten verificar si un objeto o una clase soporta una interfaz dada. La funcion se corres- ponde con el comportamiento del operador is para clases y se proyecta a1 metodo QueryInterf ace. Veamos un ejemplo: var W1: IWalker; J1: IJumper; begin W1 : = TAthlete.Create; // mds codigo. . . if Supports (wl, IJumper) then begin J1 := W1 as IJumper; Log (J1.Walk) ; end; SysUtils incluye tambien una funcion Is EqualGUID y dos funciones de conversion de cadenas a GUID y viceversa. La funcion Crea t eGUI D hasido desplazada a sysutils para que este disponible tambien en Linux (con una implernentacion personalizada, por supuesto). Por ultimo, en las ultimas versiones se han aiiadido algunas funciones mas de soporte para varias plataformas. La funcion Ad j us t LineBrea ks puede reah- zar ahora diferentes tipos de ajustes en las secuencias de retorno de carro y de avance de linea, y se han introducido nuevas variables globales para archivos de texto en la unidad System. La funcion Fi 1 eCre a t e tiene una version sobrecar- gada en la que se pueden especificar derechos de acceso a archivos a la manera Unix. La funcion ExpandFi 1 eName puede localizar archivos (en sistemas de archivos que distinguen entre mayusculas y minusculas), incluso cuando su tipo- grafia no se corresponde exactamente. Las funciones relacionadas con 10s delimitadores de ruta (barra inversa o barra oblicua) son ahora mas genericas que en las versiones precedentes de Delphi, por lo que se les han asignado nombres nuevos de acuerdo con ello. (Por ejemplo, la vieja funcion I ncludeTraling- Backslash ahora es mas conocida como IncludingTrailingPathDe- limiter). Ya que hablamos de archivos, Delphi 7 aiiade a la unidad SysUtils la funcion Get Fi levers ion, que lee el numero de version a partir de la informacion de version que se aiiade opcionalmente a un archivo ejecutable de Windows (que es por lo que esta funcion no funcionara sobre Linux). Rutinas extendidas de formato de cadenas en Delphi 7 La mayor parte de las rutinas de formato de cadenas de Delphi utilizan varia- bles globales para determinar 10s separadores de decimales y miles, 10s formatos de fecha y hora, etc. Los valores de estas variables se leen en primer lugar desde el sistema (la configuracion local de Windows) cuando arranca un programa, y se puede sobreescribir cualquiera de ellas. Sin embargo, si el usuario modifica las opciones regionales en el Panel de control mientras que el programa se esta ejecu- tado, el programa respondera a1 mensaje radiado actualizando las variables, con lo que probablemente se perderan 10s cambios introducidos directamente en el codigo. Si necesita distintos formatos de salida en diferentes partes de un mismo programa, puede aprovecharse del nuevo conjunto de rutinas sobrecargadas de formato de cadenas; admiten un parametro adicional de tipo T Format Set t ings, que incluye todas las opciones relevantes. Por ejemplo, ahora hay dos versiones de Format: function Format (const. Format : string; const Args: array of const) : string; overload; function Format (const Format: string; const Args: array of cons t ; const FormatSettings: TFormatSettings) : string; overload; Decenas de funciones disponen de este nuevo parametro adicional, que se usa en lugar de las opciones globales. Sin embargo, puede inicializarlo con las opcio- nes predeterminadas del ordenador en el que se ejecutar su programa mediante la invocation de la nueva funcion Get Locale Format Settings (solo disponi- ble en Windows, no en Linux). La unidad Math La unidad Math (matematica) csta compuesta por un conjunto de funciones matcmaticas: unas cuarenta funciones trigonomdtricas, funciones logaritmicas y esponenciales, funciones de redondeo, evaluaciones polinomicas; casi treinta fun- ciones estadisticas y una doccna dc funciones economicas. Describir todas estas funcioncs seria bastante aburrido, aunquc algunos lecto- res probablementc se cncuentren muy intcresados en las capacidadcs matematicas dc Delphi. Es por esto, que hemos decidido centrarnos en las funciones matcmati- cas prescntadas en las illtimas vcrsiones de Delphi (en particular Delphi 6) y tratar dcspues un tema espccifico que suele confundir a 10s programadorcs dc Delphi, el redondeo. Veamos algunas dc las funcioncs matematicas mas nuevas. Nuevas funciones matematicas Las vcrsioncs recicntes de Delphi ahaden a la unidad Math un numero considc- rable dc caractcristicas nuevas. Esiste soporte para constantes infinitas ( I n f i n i t y y Neg I n f i n i t y) y funciones de comparacion relac~onadas ( I s I n f i n i t e y I sNan) . junto con las nuevas funciones trigonomdtricas para cosecantcs y cotangcntes. y nuevas funciones dc conversion de angulos. Una caracteristica nluy comoda es la disponibilidad de una funcion sobrecar- gada I f Then . que devuelvc uno de dos valores posiblcs, con dependencia de una expresion booleana. (Ahora tambien hay una funcion similar disponiblc para ca- denas.) Puede usarse, por ejcmplo. para calcular el minimo dc dos valorcs: nMin := IfThen (nA < nB, na, nB) ; NOTA: La hncion IfThen es similar a1 operador ?: del lenguaje C/ C++, que es muy util porque permite reemplazar una sentencia completa i f /t h e n / e l s e por una expresion mucho mas breve, escribiendo menos codigo y declarando normalmente menos variables temporales. RandomRange y RandomFrom se pueden usar en lugar dc la traditional funcion Random para tener un mayor control de 10s valores aleatorios produci- dos por la RTL. La primera funcion devuelve un numero comprendido cntrc dos cstremos que se especifican, mientras que el segundo escoge un valor aleatorio de una matriz de numeros posiblcs quc sc pasa como un parametro. La funcion booleana I n R a n g e se puede usar para comprobar si un numero sc encuentra entrc otros dos valores. La funcion E n s u r e R a n g e , en cambio, obliga a que el valor cstd dentro del rango especificado. El valor dc retorno es el propio numero o el limite mas bajo o limite mas alto, en el caso de quc el numero sc encuentrc fuera del rango. Veamos un cjcmplo: // a c t u a s o l o s i e l v a l o r e s t d e n t r e e l m i n y e l max if InRange (value, min, max) then // s e a s e g u r a q u e e l v a l o r e s t d e n t r e min y m x value : = EnsureRange (value, min, max); Otro grupo muy util de funciones esta relacionado con las comparaciones. Los numeros de coma flotante son basicamente inexactos. Un numero de coma flotan- tc cs una aproximacion de un valor real teorico. Cuando realizamos operaciones matematicas con numeros de coma flotante, la inesactitud de 10s valores origina- les se acumula en 10s resultados. Si multiplicamos y dividimos por el mismo numero puede que no consigamos exactamente el numero original, sino uno muy proximo a 121. La funcion samevalue permite verificar si dos valores se aproxi- man lo suficiente como para ser considerados iguales. Se puede especificar el grado de aproximacion que deberian tener dos numeros o dejar que Delphi calcule un rango de error razonable para la representacion que estamos utilizando. (Por csta razon se sobrecarga la funcion.) Del mismo modo, la funcion Iszero compara un numero con cero, mediante esta misma "logica borrosa". La funcion Comparevalue usa la misma norma para 10s numeros de coma flotante per0 esta disponible tambien para enteros. Devuelve una de las tres cons- tantes LessThanValue, EqualsValue y GreaterThanValue (que se corresponden con -1,O y 1). Del mismo modo, la nueva funcion sign devuelve -1, 0 y 1 para indicar un valor negativo, cero o un valor positivo. La funcion D i v M o d es equivalente a las operaciones de division y resto, de- volviendo el resultado de la division del entero y del resto al mismo tiempo La funcion RoundTo nos permite especificar el digito de redondeo (permite, por ejemplo, redondear hasta el millar mas proximo o hasta dos decimales): RoundTo (123827 , 3 ) ; // e l r e s u l t a d o e s 1 2 4 . 0 0 0 RoundTo (12 .3827 , - 2 ) ; // e l r e s u l t a d o es 1 2 , 3 8 . . ADVERTENCIA: Fijese en que la funci6n RoundTo usa un nhnero po- sitivo para indicar la potencia de diez hash la que hay que redondear (por ejemplo, 2 para centenas) o un n6mero negativo para el numero de cifras decimales. Esto es exactamente lo contrario de la funci6n Round utilizada por hojas de calculo como Excel. TambiCn ha habido algunoscambios en las operaciones de redondeo estandar de la funcion Round: ahora, se puede controlar el mod0 en que la FPU (la Unidad de Coma Flotante de la CPU) realiza el redondeo llamando a la funcion SetRoundMode. Existen tambien funciones de control del mod0 de precision de la FPU y sus excepciones. Redondeo y dolores de cabeza La clasica funcion Round de Delphi y las mas recientes funciones RoundTo se proyectan sobre algoritmos de redondeo de la CPU y la FPU. De manera predeterminada, las CPU de Intel utilizan el redondeo bancario, que es tambidn el tipo de redondeo que se suele encontrar en aplicaciones de hojas de calculo. El redondeo bancario se basa en la suposicion de que cuando se redondean numeros que residen exactamente entre dos valores (10s numeros ,5) , a1 redon- dearlos arriba o abajo se aumenta o reduce estadisticarnente la cantidad total (en general de capital). Por este motivo, la regla del redondeo bancario indica que 10s numeros ,5 deberian redondearse arriba o abajo dependiendo de que el numero (sin decimales) sea impar o par. De esta manera, el redondeo se equilibrara, a1 menos estadisticamente. La figura 3.1 muestra un ejemplo del resultado del re- dondeo bancario. Se trata de un ejemplo diseAado para demostrar distintos tipos de redondeo. Figura 3.1. El ejernplo de redondeo, dernuestra el redondeo bancario y el aritmetico. El programa tambidn utiliza otro tipo de redondeo proporcionado por la uni- dad Math mediante la funcion SimpleRoundTo, que utiliza un redondeo arit- mktico asimetrico. En este caso, todos 10s numeros ,5 se redondean a1 valor superior. Sin embar- go, tal y como se recalca en el ejemplo de redondeo, la funcion no actua como se esperaria cuando se redondea hasta un digito decimal (es decir, cuando se pasa un segundo parametro negativo). En este caso, debido a 10s errores de representacion de 10s numeros de coma flotante, el redondeo recorta 10s valores; por ejemplo convierte 1,15 en 1,l en lugar del esperado 1.2. La solucion es multiplicar el valor por diez antes de redondear, redondearlos hasta cero digitos decimales, y despues dividirlo, como se muestra a continua- cion: (SimpleRoundTo ( d *10 , 0) / 10 ) Las unidades ConvUtils y StdConvs En la unidad ConvUtils se encuentra el nucleo del motor de conversion presen- tad0 en Delphi 6. Utiliza las constantes de conversion definidas por una segunda unidad, StdConvs. I NOTA: De 1 a 14 lib&). LII C . U I U ~ U L G I baa", 3 1 LIGUG ~ U G I l m l G j a I U u I u a u G a UG LIIGUIUU 6 1 1 su codigo, apreciara las caracteristicas disponibles en este motor. Jphi 7 supone solo una mejora en esta unidad de coaversi6n: -te parastones (la unidad britiinica de medida que es equivalente c.. ,.. "I"..:,, ,,", ..: c:,,* ,.., ,,,,;,, ..,:.a,,a*, ,a, ,, .I;..a... a, La unidad DateUtils La unidad DateUtils es una nueva coleccion de funciones relacionadas con la fecha y la hora. Engloba nuevas funciones para seleccionar valores de una varia- ble TDa t e T i m e o contar valores de un intervalo dado como: // e s c o g e r v a l o r function DayOf (const AValue : TDateTime) : Word; function HourOf (cons t AValue : TDateTime) : Word; // v a l o r en rango function WeekOf Year (const AValue : TDateTime) : Integer; function HourOfWeek (const AValue: TDateTime) : Integer; function SecondOfHour (const AValue: TDateTime) : Integer; Algunas de estas funciones son bastante extraiias, c o m o M i l l i S e c o n d O f - M o n t h o S e c o n d o f w e e k , pero 10s desarrolladores de Borland han decidido suministrar un con.junto de funciones complete, sin importar lo poco practicas que parezcan. (Realmente he utilizado algunas de estas funciones en mis e.jem- plos.) Existen funciones para calcular el valor final o inicial de un intervalo de tiem- po dado (dia, semana, mes, aiio) como la fecha actual y para verificacion del rango y consultas. Por e.jemplo: function DaysBetween (cons t ANow, AThen: TDateTime) : Integer; function WithinPastDays(const ANow, AThen: TDateTime; const ADays: Integer) : Boolean; Otras funciones abarcan el increment0 y decrement0 por parte de cada interva- lo de tiempo posible, codificando y "recodificando" (reemplazando un elemento del valor T D a t e T i m e , como el dia, por uno nuevo) y realizando comparaciones "borrosas" (comparaciones aproximadas en las que una diferencia de una milesi- ma de segundo haria que dos fechas fuesen iguales). En general, DateUtils resulta bastante interesante y no es excesivamente dificil de utilizar. La unidad StrUtils La unidad StrUtils es una nucva unidad presentada en Delphi 6 con algunas nucvas funciones relacionadas con cadenas. Una de las caractcristicas clave dc csta unidad es la existencia de muchas funcioncs de comparacion de cadenas. Hay funciones basadas en un algoritmo "soundex" (Ans iResembleText ) ; y algu- nas que ofrecen la capacidad de realizar busquedas en matrices de cadenas (Ans iMat c h T e x t y Ans i IndexTex t ) , localizar y sustituir subcadenas (como NOTA: Soundex es un algoritmo para comparar nombres basados en el mod0 en que suenan y no en el modo en que se deletrean. El algoritmo calcula un numero para cada sonido de la palabra, de modo que comparan- do dos de esos numeros se puede decidir si dos nombres suenan igual. El sistema lo aplic6 por pimerzi vez en 1880 la U.S. Bureau of the census (La Oficina del Censo de EEUU); se patent6 en 1918 y en la actualidad es de dominio publico. El codigo soundex es un sistema de indexado que traduce 1 ---- - .... -:I:__ 3 - - . . -L -_ - - - _ _ L _ - - _ L- 3 - ..-A l - L - _ A _ - _ nomores a un coalgo ae cuatro caracteres Iormaao por una m r a y rres numeros. Puede encontrar mas information a1 respecto en www.nara.gov/ genealogylcoding . html . Mas a116 de las comparaciones, otras funciones proporcionan una prueba en dos direcciones (la simpatica funcion I f T h e n , similar a la que ya hemos visto para 10s numeros), duplican e invierten cadenas y sustituyen subcadenas. La ma- yoria de estas funciones de cadena se aiiadieron por comodidad para 10s progra- madores en Visual Basic que se pasaban a Delphi. Hemos utilizado algunas de dichas funciones en el ejemplo StrDemo, que usa tambien algunas conversiones de booleano a cadena definidas dentro de la unidad SysUtils. El programa en realidad es algo mas que una prueba para unas cuantas funciones. Por ejemplo, se usa la comparacion "soundex" entre las cadenas introducidas en dos cuadros de edicion. convierte el booleano resultante en una cadena y lo muestra: ShowMessage (BoolToStr (AnsiResemblesText (EditResemblel-Text, EditResemble.2 .Text) , True) ) ; El programa tambien utiliza las funciones An s iMa t c h T e x t y Ans i I ndexTex t , tras haber rellenado una matriz dinamica de cadenas (deno- minada s t r A r r a y ) con 10s valores de las cadenas del interior del cuadro de lista. Se podria haber utilizado el metodo I n d e x o f de la clase T S t r i n g s , que es mas sencillo, pero esto habria anulado el proposito del ejemplo. Las dos com- paraciones de lista se realizan del siguiente modo: procedure TForml.ButtonMatchesClick(Sender: TObject); begin ShowMessage (BoolToStr (AnsiMatchText(EditMatch.Text, strArray) , True) ) ; end; procedure TForml.ButtonIndexClick(Sender: TObject); var m a t c h : Integer; begin m a t c h := AnsiIndexText (EditMatch.Text, strArray) : ShowMessage ( IfThen ( m a t c h >= 0, ' C o r r e s p o n d e a 1 n u r n e r o d e c a d e n a ' + IntToStr ( m a t c h ) , ' N o c o r r e s p o n d e ' ) ) ; end; Fijese en el uso de la funcion I f Then en las ultimas lineas de codigo; tiene dos cadenas de salida alternativas, que dependen del resultado del test inicial (nMatch >= 0). Tres botones adicionales realizan llamadas sencillas a otras tres funciones nuevas, con las siguientes lineas de codigo (una para cada una):// r e p i t e ( 3 veces) u n a c a d e n a ShowMessage (Dupestring (EditSample.Text, 3) ) ; // i n v i e r t e l a c a d e n a ShowMessage (Reversestring (EditSample.Text)); // e s c o g e u n a c a d e n a a l e a t o r i a ShowMessage (RandomFrom (strArray)); De Pos a PosEx Delphi 7 aporta su granito de arena a la unidad StrUtils. La nueva funcion PO s E X resultara muy practica para muchos desarrolladores y merece que hable- mos de ella. Cuando se buscan multiples apariciones de una cadena dentro de otra, una solucion clasica de Delphi era utilizar la funcion Pos y repetir la bus- queda sobre la parte restante de la cadena. Por ejemplo, podria contar el numero de apariciones de una cadena dentro de otra con un codigo como este: function CountSubstr (text, sub: string) : Integer; var nPos: Integer; begin Result := 0; nPos := Pos (sub, text) ; while nPos > 0 do begin Inc (Result) ; text : = Copy (text, nPos + Length (sub) , MaxInt) ; nPos : = Pos (sub, text) ; end; end; La nueva funcion Po SEX permite especificar la posicion de comienzo de la busqueda dentro de una cadena, de manera que no se necesita modificar la cadena original (que supone una ligera perdida de tiempo). Por eso. el codigo anterior puedc simplificarsc como: function C o u n t S u b s t r ( t e x t , s u b : s t r i n g ) : I n t e g e r ; var nPos : I n t e g e r ; begin R e s u l t : = 0: npos : = PosEx ( s u b , t e x t , 1 ) ; // predeterminado while nPos > 0 do begin Inc ( R e s u l t ) ; npos : = PosEx ( s u b , t e x t , nPos + Length ( s u b ) ) ; end ; end ; Ambas porciones de codigo se utilizan de una manera trivial en el ejemplo S t r Demo comentado anteriormente. La unidad Types La unidad Types (de tipos) almacena tipos de datos comunes a diversos siste- mas operativos. En las anteriores versiones de Delphi, la unidad de Windows definia 10s mismos tipos; ahora se han desplazado a esta unidad comun, compar- tida por Delphi y Kylix. Los tipos definidos aqui son sencillos y engloban, entre otros. las estructuras de registro TPoint, T R e c t y TSmallPoint mas sus tipos de punter0 relacionados. ERTENCIA: Fijese en que tendri que actualizar 10s programas Delphi uos que hagan referencia a T R e c t o TPo i n t , dadiendo la unidad s en la sentencia uses: de no ser asi- 10s Droeramas no se comoilarh. La unidad Variants y VarUtils Las unidades Variants (de variantes) y VarUtils son dos nuevas unidades pre- sentadas en Delphi 6 que agrupan las partes de la biblioteca relacionadas con variantes. La unidad Variants contiene codigo generico para variantes. Algunas rutinas de esta unidad han sido desplazadas aqui desde la unidad System. Las funciones abarcan soporte generico de variantes, matrices variantes, copiado de variantes y convcrsiones de matriz dinarnica a matriz variante. Tambien esta la clase T C u s t omVa r i an t T y p e , que define 10s tipos de datos variantes personalizables. La unidad Variants es totalmente independiente de la plataforma y utiliza la unidad VarUtils, que contiene codigo dependiente del SO. En Delphi, esta unidad usa las API dcl sistcma para manipular datos de variantcs: cn Kylis usa codigo particularizado quc Ic proporciona la biblioteca RTL. NOTA: En Dclphi 7, estas unidades se han arnpliado y se han solucionado algunos problcmas. La impiementacion de variantes ha sido remodelada a conciencia para mejorar la velocidad de esta tecnologia y reducir la ocupa- cion en memoria de su codigo. Un arca cspccifica quc ha visto una me-jora significativa en Dclphi 7 es la capacidad de controlar cl comportamicnto de las implemcntacioncs dc variantes, en particular las rcglas dc comparacion. Delphi 6 supuso un cambio en el codigo dc variantes de mancra quc 10s valores null no podian compararsc con otros valores. Estc comportamiento es correct0 desde un punto de vista formal, de manera cspccifica para 10s campos de un conjunto dc datos (un area en que se usan mucho variantcs). pcro cste cambio tuvo cl cfccto colateral de romper el codigo csistcntc. Ahora sc puede controlar cstc comportamiento mediante el uso dc las variablcs globales Nu1 lEqual it yRule y Nu1 lMagni tudeRule; cada una dc las cualcs toma uno de 10s siguicntcs valorcs: ncrError : Cualquicr tipo de comparacion provoca quc sc levante una es- cepcionj \,a quc no pucdc compararse un valor indcfinido: cste cra cl com- portamicnto prcdctcrminado (nuevo) en Dclphi 6 . ncrstr ict : Cualquier tipo de comparacion falla siempre (devuelvc False), sin importar 10s valores. ncrLoose: Las comprobaciones de igualdad solo tienen cxito cntrc valores nulos (un valor nulo es distinto de cualquicr otro valor). En las compara- cioncsi 10s valorcs nulos se consideran como valorcs vacios o cero. Otras opcioncs con10 NullStrictConvert y NullAsStringValue controlan cl mod0 cn quc sc rcalizan las comparaciones en caso dc valorcs nulos. Un buen consejo cs cspcrimcntar con el e.jcmplo VariantComp quc se cncuentra disponiblc mas adelantc. Como mucstra la figura 3.2. este programa dispone de un formulario con un RadioGroup que se puede utilizar para modificar 10s \ d o - res de las variablcs globales NullEqualityRule y NullMagnitudeRule y unos cuantos botoncs para rcalizar diversas comparacioncs. Variantes personalizadas y numeros complejos La posibilidad dc ampliar el sistema dc tipos con variantes personalizadas es una extension rcciente del concept0 dc variantcs. Nos permite definir un nuevo tipo de datos quc, cn oposicion a la clase; sobrccarga 10s operadores aritmiticos estandar. Una variantc es un tipo que manticnc tanto la especificacion de tipo como el valor real. Una variante puedc contener una cadcna. otra pucde contener un numero. El sistema define conversiones automaticas entre tipos dc variantes (como variantes personalizadas), lo que le permite mezclarlas en las opcraciones. Esta flexibilidad tiene un coste muy alto: las operaciones con variantes son mu- cho mas lentas que las rcalizadas con tipos originales y las variantcs utilizan memoria adicional. Figura 3.2. El forrnulario del ejernplo VariantCornp en tiempo de diseiio. Como ejemplo dc un tip0 de variante personalizada, Delphi aporta una intere- sante definicion para 10s numeros complejos en la unidad VarCmplx (disponi- bles en formato de codigo fuente en el directorio Rtl\Common). Se pueden crear variantes complejas utilizando una de las funciones sobrecargadas Varcomplex- Create y usarlas en cualquier espresion, como se demuestra en el siguiente fragment0 de codigo: var vl, v2: Variant; begin vl : = VarComplexCreate (10, 12); v2 := VarComplexCreate (10, 1) ; ShowMessage (vl + v2 + 5) ; Los numeros complejos se definen en realidad utilizando clases, per0 la super- ficie quc adoptan es la de variantes, mediante la herencia de una nueva clase de la clase TCus tomVar iantT ype (definida en la unidad Variants), sobrescritura de una serie de funciones abstractas virtuales y creacion de un objeto global que se encarga del registro dentro del sistema. Ademas de estas definiciones internas, la unidad incluye una larga lista de rutinas para operar con variantes, como las operaciones matematicas y trigonometricas. ADVERTENCIA: Construir una variante personalizada no es una tarea nada sencilla y apenas se pueden encontrar razones para usarlas en lugar de objetos y clases. De hecho, con una variante personalizada se tiene la ven- . . I I . I I I . . l r raja ae usar la soorecarga ae operaaores en las esrrucruras ae aaros, per0 se pierde la verification en tiempo de cornpilacion, se hace que el codigo sea z l e n t o v i s t i c a s be orlentaci6n a 04et.o~ y se ha de escribir un codigo bastante mas complejo. Las unidades DelphiMM y ShareMem Las unidades DelphiMM y ShareMem estan relacionadas con la gestion de memoria. El administrador de memoria estandar de Delphi se declara en launidad S y s tern. La unidad DelphiMM define una bibliotcca de administrador de mcmoria al- tcrnativa para utilizarla a1 pasar cadenas de un ejecutable a una DLL (una biblio- tcca de enlace dinamico de Windows), ambas construidas en Delphi. Esta biblioteca dc administrador de memoria se encuentra compilada de manera predeterminada cn el archivo de biblioteca Borlndmrn. dl1 que habra que distribuirjunto con el programa. La interfaz de este administrador de memoria se define en la unidad ShareMem. Esta es la unidad que se habra de incluir (es obligatoria como primera unidad) en 10s proyectos del Gecutablc y de la biblioteca (bbibliotecas). _LC--- - NOTA: Al contrario que Delphi, Kylix no dispone de unidades DelphiMM y ShareMem, ya que la gestion de memoria se proporciona en las bibliote- cas nativas de Linux (en particular, Kylix utiliza malloc de glibc) y por eso se comparte efectivamente entre distintos modulos. Sin embargo, en Kylix, las aplicaciones con multiples modulos deben utilizar la unidad ShareExcept, que permite que las excepciones lanzadas en un modulo se reflejen en otro. Unidades relacionadas con COM ComConst, ComObj y ComServ proporcionan soporte COM a bajo nivel. Es- tas unidades no son realmente parte de la RTL, desde un cierto punto de vista, asi que no se comentaran aqui. Mas adelante encontrara informacion detallada, per0 baste aiiadir que estas unidades no han cambiado mucho en las mas recientes versiones de Delphi. Convertir datos Delphi incluye un nuevo motor de conversion, definido en la unidad ConvUtils. El motor por si mismo no incluye definicion alguna de las unidades de medida reales; en cambio, posee una serie de funciones principales para 10s usuarios finales. La funcion clave es la llamada de conversion, la funcion Convert. Sencillamente, nosotros proporcionamos la cantidad, las unidades en las que se expresa y las unidades a las que queremos que se conviertan. Lo siguiente convertiria una temperatura de 3 1 grados centigrados a Fahren- heit: Convert (31, tucelsius, tuFahrenheit) Una version sobrecargada de la funcion convert permite convertir valores que poseen dos unidades, como la velocidad (que tiene una unidad de longitud y una unidad de tiempo). Por ejemplo, se pueden convertir kilometros por hora en metros por segundo con esta llamada: Convert (20, duKilometre, tuHours, duMeters, tuseconds) Otras funciones de la unidad permiten convertir el resultado de una suma o una resta, verificar si las conversiones se pueden aplicar e incluso listar las familias y unidades de conversion disponibles. En la unidad StdConvs, se proporciona un conjunto predefinido de unidades de medida. Esta unidad tiene familias de conversion y un impresionante numero de valores, como en el siguiente extracto: // U n i d a d e s d e c o n v e r s i o n d e d i s t a n c i a s // l a u n i d a d b d s i c a d e m e d i d a es e l m e t r o cbDistanke: TConvFamily; duAngstroms : TConvType; dulrlicrons: TConvType; dulrlillimeters: TConvType; duMeters : TConvType; duKilometers: TConvType; duInches: TConvType; duMiles: TConvType; duLightYears: TConvType; duFurlongs: TConvType; duHands : TConvType ; duPicas: TConvType; Esta familia y las diversas unidades se registran en el motor de conversion en la parte de inicializacion de la unidad y proporcionan ratios de conversion (guar- dados como una serie de constantes, como MetersPerInch en el siguiente codigo): cbDistance : = RegisterConversionFamily('Distancia'); duAngstroms : = RegisterConversionType(cbDistance, ' A n g s t r o m s ' , 1E-10) ; dmillimeters : = RegisterConversionType(cbDistance, ' M i l i m e t r o s ' , 0.001) ; duInches : = RegisterConversionType(cbDistance, ' P u l g a d a s ' , MetersPerInch) ; Para probar el motor de conversion, creamos un e.jemplo generic0 (ConvDemo) quc permite traba.jar con todo el conjunto de conversiones disponibles. El progra- ma rellena un cuadro combinado con las familias de conversion disponibles y un cuadro de lista con las unidades disponibles de la familia activa. Este es el codigo: procedure TForml. Formcreate (Sender: TObject) ; var i: Integer; begin GetConvFamilies (aFamilies) ; for i : = Low (aFamilies) to High (aFamilies) do ComboFamilies.1tems.Add (ConvFamilyToDescription (aFamilies [i] ) ) ; // obtiene el primer0 y lanza el evento ComboFamilies.Item1ndex : = 0; ChangeFamily (self) ; end ; procedure TForml.ChangeFamily(Sender: TObject); var aTypes : TConvTypeArray; 1: Integer; begin ListTypes .Clear; CurrFamily : = aFamilies [ComboFamilies.ItemIndex]; GetConvTypes (CurrFamily, aTypes) ; for i : = Low(aTypes) to High(aTypes) do ListTypes.Items.Add (ConvTypeToDescription (aTypes[i])); end; Las variables aFamilies y CurrFamily se declaran en la parte privada del formulario dcl siguiente modo: aFamilies: TConvFamilyArray; CurrFamily: TConvFamily; En este punto, un usuario puede introducir dos unidades de medida y una cantidad en 10s cuadros de edicion correspondientes del formulario, como se pue- de ver en la figura 3 3. Para que la operacion sea mas rapida, es posible seleccio- nar un valor de la lista y arrastrarlo hasta uno de 10s dos cuadros de edicion de tipo. pulsado el boton lzquierdo del raton mientras se arrastra el elemento sobre una de las cajas de edicion que se encuentran en el centro del formulario. -- - para c o i k g z c & p a z , hay q z - ~ a propiedad DragMode de la caja de lista (el componente fuente) con el valor dmAutomatic e implementar 10s eventos OnDragOver y OnDragDrop de las cajas de edicion objetivo (las dos cajas de edicion se encuentran conectadas a 10s mismos manejadores de eventos, compartiendo el mismo codigo). En el primer mktodo, el programa indica que las cajas de edicion siempre aceptan la operaci6n de arrastre, sin importar la fuente. En el se- gundo mktodo, el programa copia el texto seleccionado en la caja de lista (el control source para la operation de arrastre) a la caja de edicion que haya disparado el evento (el objeto Sender). Este es el cd igo para 10s dos metodos: procedure TForml.EditTypeDragOver(Sender, Source: TObject; X I Y: Integer; State: TDragState; var Accept: Boolean); begin Accept := True; end; procedure TForml.EditTypeDragDrop(Sender, Source: TObject; X I Y: Integer) ; begin ; (Sender ar TEdit) .Text := (Source as TListBox) . Items [ (Source as TListBox) . ItemIndexl; end ; Eamiks Inslruciimr hap types D~stance Snnple Ted I lrorn lM to dl boxes. enlm am* lvper Base lype: &&: LghlYeats Cmlirnetus 1100 Parsecs Fathom Furbngs Qerlinalica Type Cmvwted Am& Hands Paces Cham I 1 Figura 3.3. El ejemplo ConvDemo en tiempo de ejecucion. Las unidades habran de corresponderse con aquellas disponibles en la familia activa. En caso de error, el texto de 10s cuadros de edicion de tipo aparecc en rojo. Este es el efecto de la primera parte del metodo DoConver t del forinulario, que se activa desde el momento en que el valor de uno de 10s cuadros de edicion para las unidades o la cantidad cambian. Despues de verificar 10s tipos de 10s cuadros de edicion, el metodo DoConver t realiza la conversion real y muestra el resul- tad0 en el cuarto cuadro de edicion, que esta en gris. En caso de errores, aparece- ra el mensaje correspondiente en el mismo cuadro. Veamos el codigo: procedure TForml.DoConvert(Sender: TObject); var BaseType, DestType: TConvType; begin // o b t i e n e y v e r i f i c a e l t i p o b d s i c o i f not DescriptionToConvType(CurrFamily, EditType-Text, BaseType) then EditType.Font.Color : = clRed else EditType.Font.Color : = clBlack; // o b t i e n e y v e r i f i c a e l t i p o d e d e s t i n o i f not DescriptionToConvType (CurrFamily, EditDestination-Text, DestType) then EditDestination.Font.Color := clRed e l s e EditDestination.Font.Co1or : = clBlack;i f (DestType = 0) or (BaseType = 0) then EditConverted.Text : = ' T i p o n o v d l i d o ' else EditConverted.Text : = FloatToStr (Convert ( StrToFloat (EditAmount-Text), BaseType, DestType)); end; Si todo esto no resulta interesante, hay que tener en cuenta que todos 10s tipos de conversion proporcionados en el ejemplo son solo una muestra: se puede perso- nalizar completamente el motor para que proporcione las unidades de medida en que se este interesado, como se comentara a continuacion. iConversiones de divisas? La conversion de divisas no es exactamente lo mismo que la conversion de unidades de medida, ya que 10s valores de las divisas cambian constantemente. En teoria, se puede registrar un valor de cambio en el motor de conversion de Delphi. De vez en cuando, se comprobara el nuevo indice de cambio, se desregistrara la conversion ya existente y se registrara la nueva. Sin embargo, mantener la tasa de cambio real implica modificar la conversion tan a menudo que la operacion po- dria no tener mucho sentido. Ademas, habra que triangular conversiones: hay que definir una unidad base (probablemente el euro, si vive en Europa) y convertir a/ y desde esta divisa incluso si la conversion se realiza entre dos divisas distintas. Por ejemplo, antes de la adopcion del euro como divisa de la Union Europea, lo mejor era utilizar esta divisa como base para las conversiones entre las divisas de 10s estados miembros, por dos motivos. En primer lugar, 10s tipos de cambio eran fijos. En segundo lugar, la conversion entre las divisas euro se rcalizaban legal- mente convirtiendo una cantidad a euros y convirtiendo dcspues esa cantidad en euros a la otra divisa. el comportamiento exacto del motor de conversion de Delphi. Existe un pequeiio problema: debcria aplicarse un algoritmo de redondco en cada paso de la conversion. Considerarcmos este problema mas adelante, tras ofrccer el codigo base para integrar las divisas euro con cl motor de conversion de Delphi. NOTA: El ejemplo Conver I t disponible entre 10s ejemplos Delphi ofre- ce soporte para las conversiones a1 euro, utilizando un enfoque de redondeo ligeramente distintos, aunque no tan precis0 como el requerido por las re- glas de conversion de divisas europeas. Aun asi, resulta aconsejable mante- ner este ejemplo porque es bastante instructive en relacion con la creaci6n de un nuevo sistema de medida. El ejemplo, llamado EuroConv, muestra como registrar cualquier nueva uni- dad de medida con el motor. Siguiendo la plantilla que proporciona la unidad S tdConvs creamos una nueva unidad (llamada EuroConvCons t). En la sec- cion de la interfaz, declaramos las variables para la familia y las unidades especi- ficas: interface var // U n i d a d e s d e C o n v e r s i o n d e D i v i s a s E u r o p e a s c b E u r o C u r r e n c y : TConvFamily; cuEUR: TConvType; cuDEM: TConvType; // Alemania cuESP: TConvType; // Espafia cuFRF: TConvType; // P r a n c i a // y e l r e s t o . . . La seccion de implementation de la unidad define constantes para diversas tasas de conversion oficiales: implementation cons t DEMPerEuros = 1 , 9 5 5 8 3 ; ESPPerEuros = 166 ,386 ; FRFPerEuros = 6 ,55957 ; // y e l r e s t o . . . Finalmente, el codigo de inicializacion de la unidad registra la familia y las diversas divisas, cada una con su propio tip0 de cambio y un nombre legible: i n i t i a l i z a t i o n / / Tipo de la familia de divisas europeas cbEuroCurrency : = RegisterConversionFamily ( ' D i v i s d s E u r o ) ; cuEUR := RegisterConversionType( cbEuroCurrency, 'EUR', 1) ; cuDEM : = RegisterConversionType( cbEuroCurrency, IDEM', 1 / DEMPerEuros) ; cuESP : = RegisterConversionType( cbEuroCurrency, 'ESP', 1 / ESPPerEuros); cuFRF : = RegisterConversionType( cbEuroCurrency, 'FRF' , I / FRFPerEuros) ; NOTA: El motor utiliza como factor de conversion la cantidad de la unidad base necesaria para obtener las unidades secundarias, con una constante como Meters P e r Inch, por ejemplo. El tipo es thdar de las divisas euro se define al rev&. Por este motivo, se han mantenido las constantes de conversion con 10s valores oficiales (como DEMPerEuros) y se han pasa- do a1 motor como fracciones ( I / DEMPerEuros). Tras registrar esta unidad, se pueden convertir 120 marcos alemanes en liras italianas de esta manera: Convert (120, cuDEM, cuITL) El programa de ejemplo hace algo mas: ofrece dos cajas de lista con las divisas disponibles, extraidas como en el ejemplo anterior, y cajas de edicion para el valor de entrada y el resultado final. La figura 3.4 muestra el formulario. Belg~an Francs [BEF) 1 Cmml Dutch Gu~lders [NLG] Austrian Sch~ll~ngi [ATS] Poituguese Escudos [PTE) F~nn~sh Marks [FIM] G~eek Drachms [GRD] Luembou'g F'-f [LUFI I labn L#e [ITLJ ' Belgian Francs [BEF) Dutch Gulders [NLG) Auslnan Sch~nlngs (ATS] Porluwese Escudos lPTEl ~ m n & ~ a r k o [FIM] ' Greek Drachmas (GRDI Luxembourg Francs [LUF) Figura 3.4. La salida del ejemplo EuroConv, que muestra el uso del motor de conversion de Delphi con una unidad de medida personalizada. El programa funciona bien per0 no perfectamente, ya que no se aplica el re- dondeo correcto; deberia redondearsc no solo el resultado final de la conversion sin0 tambien el valor intermedio. Mediante el motor de conversion no se puede realizar este redondeo directamente de manera sencilla. El motor permite ofrecer una funcion de conversion o una tasa de conversion particularizadas. Pero escri- bir funciones de conversion identicas para todas las divisas parece una mala idea, asi que hemos escogido un camino diferente. (Puede ver ejemplos de funciones de conversion personalizadas en la unidad St dCo nvs, en la seccion relacionada con las temperaturas.) En el ejemplo EuroConv, aiiadimos a la unidad con las tasas de conversion una funcion EuroConv personalizada que realiza la conversion correcta. Lla- mando simplemente esta funcion en lugar de la funcion Convert estandar con- seguiremos el efecto deseado (y no parece existir ningun inconveniente, ya que en este tip0 de programas es extraiio mezclar divisas con distancias o temperaturas). De manera alternativa, podriamos haber heredado una nueva clase a partir de TCo nvT ype Fact o r, proporcionando una nueva version de 10s metodos FromCommon y Tocommon; o haber utilizado la version sobrecargada de Regi sterconvers ionType que acepta estas dos funciones como parametros. Sin embargo, ninguna de estas tecnicas habria permitido enfrentarse a casos espe- ciales, como la conversion de una divisa a si misma. Este es el codigo de la funcion EuroConv, que utiliza la funcion inter- na EuroRound para redondear a1 numero de digitos especificado en el pa- rametro Decimals (que debe estar entre 3 y 6, de acuerdo con las reglas oficia- les): type TEuroDecimals = 3. . 6 ; function EuroConvert (const AValue: Double; const AFrom, ATo: TConvType; const Decimals: TEuroDecimals = 3): Double; function EuroRound (const AValue: Double): Double; begin Result :=AValue * Power (10, Decimals) ; Result : = Round (Result) ; Result : = Result / Power (10, Decimals) ; end ; begin // comprobacion d e l caso e s p e c i a l : s i n convers ion if AFrom = ATo then Result := AValue; else begin / / conversion a1 euro y redondeo Result := ConvertFrom (AFrom, AValue) ; Result := EuroRound (Result) ; / / conversion a la divisa y nuevo redondeo Result : = ConvertTo (Result, ATo) ; Result : = EuroRound (Result) ; end ; end ; Por supuesto, podria desearse ampliar el ejemplo para ofrecer conversion a otras divisas no europeas, tal vez tomando 10s valores automaticamente desde un sitio Web. Gestion de archivos con SysUtils Para acceder a archivos y a la informacion de archivos, generalmente puede confiarse en las funciones estandar disponibles en la unidad SysUtils. Confiar en estas tradicionalesbibliotecas de Pascal hace que el codigo sea mas transportable entre diferentes sistemas operativos (aunque deberian considerarse con mucho cuidado las diferencias en las arquitecturas del sistema de archivos, en particular la cuestion de las mayusculas y las minusculas en la plataforma Linux). Por ejemplo, el ejemplo FilesList utiliza la combinacion F i n d F i r s t , F i n d N e x t y F i n d C l o s e para obtener a partir de una carpeta una lista de archivos que se corresponden con un filtro, con el mismo codigo que se podria utilizar en Kylis y Linux. La figura 3.5 muestra el aspect0 de este ejemplo. I D \md7code\O2\ClassRel\ClassRel dpr D \md7cude\02\C1ealeComps\CreateComps dp~ ~ : \ r n d 7 c o d e \ 0 2 \ ~ a l e ~ r d p \ ~ a l e ~ r d ~ d ~ D:W7code\U2\Dalesl\Datesl dpr D-\md7code\OZ\DBGridCol\DBG11dCol,dpr D \rnd7coJe\02\Erro1Log\ErrorLog dpf D \md7code\02\Excepbun1\Excepbornl.dpr D.\md7wde\O2\F~mPfopU01mProp.dp D:\md7code\02\1IDrecl1veUID~ecl1ve.dp1 D:Lnd7code\02\lnllDerno\lnllDemo dpr D hd7code\02\NewDale\NewDale dp~ D \md7c~de\02\Polu4n1mals\Polu9nmals.&r Figura 3.5. Un ejernplo de la salida de la aplicacion FilesList. El codigo siguiente aiiade 10s nombres de archivo a la caja de lista llamada 1 b F i l e s : procedure TForml.AddFilesToList(Filter, Folder: string; Recurse : Boolean) ; var sr: TSearchRec; begin if FindFirst (Folder + Filter, faAnyFile, sr) = 0 then repeat 1bFiles. Items .Add (Folder + sr .Name) ; until FindNext (sr) <> 0; FindClose (sr) ; Si el parametro Re c u r s e se activa, el procedimiento A d d F i l e s T o L i s t obtiene una lista de subcarpetas inspeccionando 10s archivos locales de nuevo y autoinvocandose para cada una de las subcarpetas. La lista de carpetas se coloca en un objeto de lista de cadenas, con el codigo siguiente: procedure GetSubDirs (Folder: string; sList: TStringList); var sr: TSearchRec; begin i f FindFirst (Folder + ' * . * I , faDirectory, sr) = 0 then try repeat i f ( sr.Attr and faDirectory) = faDirectory then sList .Add (sr .Name) ; u n t i l FindNext (sr) <> 0; f i n a l l y Findclose (sr) ; end ; end ; Finalmente, el programa utiliza una interesante tecnica para solicitar a1 usua- rio que seleccione el directorio inicial para la busqueda de archivos, mediante una llamada a1 procedimiento S e l e c t D i r e c t o r y . (Vease la figura 3.6.) i f SelectDirectory ('Seleccione una carpeta' , I , , CurrentDir) then . . . Figura 3.6. El cuadro de dialog0 del procedimiento select ~ i r e c t o r y , utilizado por la aplicacion FilesList. La clase TObject La definicion de la clase T O b j e c t es un elemento clave de la unidad System, "la madre de todas las clases Delphi". Cada clase del sistema es una subclase de la clase TObj e c t , directa (si se especifica TOb j e c t como la clase base), impli- citamente (a1 no indicarse la clase base), o indirectamente (cuando se especifica otra clase como antecesor). Toda la jerarquia de las clases de un programa en Pascal orientado a objetos posee una raiz unica. Esta permite usar el tipo de datos TOb j e c t como substituto del tipo de datos de cualquier tipo de clase del siste- ma. Por ejemplo, 10s controladores de eventos de componentes normalmente tienen un parametro Sender de tipo TObj e c t . Esto significa sencillamente que el objeto Sender puede pertenecer a cualquier clase, puesto que cada clase se deriva en ultima instancia de Tob j e c t . El inconveniente mas habitual de esta tecnica es que para trabajar sobre el objeto, es necesario conocer su tipo de datos. De hecho, cuando se tiene una variable o un parametro del tipo TObj e c t , se le pueden aplicar solo 10s metodos y propiedades definidas por la propia clase TOb j e c t . Si esta variable o parametro se refiere por casualidad a un objeto del tipo TButton, por ejemplo, no se puede acceder directamente a su propiedad Caption. La solucion a este problema recae en el uso de 10s operadores de conversion segura de tipos siguientes o en 10s operadores de informacion de tip0 en tiempo de ejecucion (RTTI) (10s operadores i s y as). Existe otra tecnica. Para cualquier objeto, se puede llamar a 10s metodos defi- nidos en la misma clase TObj e c t . Por ejemplo, el metodo ClassName devuel- ve una cadena con el nombre de la clase. Debido a que es un metodo de clase, se puede aplicar tanto a un objeto como a una clase. Supongamos que hemos defini- do una clase TButton y un objeto Button1 de dicha clase. En ese caso, las siguientes sentencias tendran el mismo efecto: Text : = Button1.ClassName; Text := TButton.ClassName; Hay ocasiones en las que es necesario usar el nombre de una clase, per0 tam- bien puede ser util recuperar una referencia de clase a la propia clase o a su clase basica. La referencia de clase, de hecho, permite trabajar en la clase en tiempo de ejecucion, mientras quc cl nombre de clase es simplemente una cadena. Podemos obtener estas referencias de clase con 10s metodos Clas sType y Class Parent. El primer0 devuelve una referencia de clase a la clase del objeto, el segundo a su clase basica. Cuando tengamos una referencia de clase, podemos aplicarle cual- quier metodo de clase TOb j ec t , por ejemplo, para llamar a1 metodo ClassName. Otro metodo que podria resultar util es Ins t a n c e s i z e , que devuelve el tamaiio en tiempo de ejecucion de un objeto. Aunque se podria pensar que la funcion global Sizeof ofrece dicha informacion, esa funcion en realidad de- vuelve el tamaiio de una referencia al objeto (un punter0 que siempre tiene cuatro bytes), en lugar del tamaiio del objeto en si. En el listado 3.1, se puede encontrar la definicion completa de la clase TOb j e c t , extraida de la unidad System. Ademas de 10s metodos mencionados, fijese en que I n h e r i t s From proporciona una comprobacion muy similar a la del operador is, pero que se puede aplicar tambien a clases y referencias de clase (mientras el primer argument0 dc i s habra dc ser un objeto). Listado 3.1. La definicion de la clase TObject (en la unidad System de la RTL). type TObject = class constructor Create; procedure Free; class function Init Instance (Instance: Pointer) : TOb j ect; procedure CleanupInstance; function ClassType: TClass; class function ClassName: ShortString; class function ClassNameIs( const Name: string): Boolean; class function Classparent: TClass; class function ClassInfo: Pointer; class function Instancesize: Longint; class function InheritsFrom(AC1ass: TClass) : Boolean; class function MethodAddress (const Name : ShortString) : Pointer; class function MethodName(Address: Pointer): ShortString; function FieldAddress (const Name: ShortString) : Pointer; function GetInterf ace (const IID: TGU1D;out Obj) : Boolean; class function GetInterfaceEntry( const IID: TGUID): PInterfaceEntry; class function GetInterfaceTable: PInterfaceTable; function SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer) : HResult; virtual; procedure Afterconstruction; virtual; procedure BeforeDestruction; virtual; procedure Dispatch(var Message); virtual; procedure DefaultHandler(var Message); virtual; class function NewInstance: TObject; virtual; procedure FreeInstance; virtual; destructor Destroy; virtual; end; I tipo en tiempo de ejecucib (RTTI) dc h clase Estos metodos de TOb j ect estan disponibles para 10s objetos de cada clase, puesto que TOb j ect es el antcccdente comun de cada clase. Veamos como pode- mos usar estos metodos para acceder a informacion de clase: procedure TSenderForm.ShowSender(Sender: TObject); begin Memo1 .Lines .Add ( 'Nombre de clase:' ' + Sender .ClassName) ; if Sender.ClassParent <> nil then Memol. Lines .Add ( ' Clase p a d r e : ' + Sender.ClassParent.ClassName); Memol .Lines .Add ( ' T a m d o de la instancia : ' + IntToStr (Sender-Instancesize)); end; El codigoverifica si la Classparent es nil, en caso de que se este utili- zando realmente una instancia del tipo TOb ject, que no tiene tipo basico. Este metodo Showsender es parte del ejemplo I f Sender del CD. El me- todo esta conectado con el evento OnClic k de diversos controles: tres botones, una casilla de verificacion y un cuadro de edicion. Cuando hacemos clic sobre cada control, se recurre a1 metodo Showsender con el control correspondiente como remitente. Uno de 10s botones es en realidad un boton Bitmap, un objeto de una subclase TButton. Se puede ver un ejemplo de este programa en tiempo de ejecucion en la figura 3.7 Class Name TButton Parent Chss: TBultonConlrd Instance Sin: 536 TButton ClassType Sender inherits Lom TButton Sender is a TButlm Class Name. TBitBtn Palent Class TBulton Instance Size: 560 Sender &its from TButton Sendm is a TBulton Class Name TCheckBox Parent Class TCustornCheckBon Indance Size. 536 Class Nam: TEB Paent Class: TCustomEdit lnstace Size: 544 Figura 3.7. El resultado del ejemplo Ifsender Se pueden usar otros metodos para realizar pruebas. Por ejemplo, se puede verificar si el objeto Sender es de un tipo especifico con el siguiente codigo: if Sender-ClassType = TButton then . . . Tambien se puede verificar si el parametro Sender se corresponde a un obje- to dado, con este test: if Sender = Button1 then.. . En lugar de verificar una clase o objeto concreto, sera necesario, por lo gene- ral, comprobar la compatibilidad de tip0 de un objeto con una clase dada, es decir, sera necesario verificar si la clase del objeto es una clase determinada o una clase de sus subclases. Esto permite saber mejor si se puede trabajar sobre el objeto con 10s metodos definidos para dicha clase. Esta comprobacion se puede realizar utilizando el metodo InheritsFrom, a1 que tambien se llama cuando se usa el operador is. Las siguientes pruebas son equivalentes: if Sender. InheritsFrom (TButton) then . . . if Sender is TButton then . . . Mostrar informacion de clase Hemos ampliado el ejemplo If Sender para mostrar una lista completa de clases basicas de un objeto o clase dados. De hecho, cuando tengamos una refe- rencia de clase, se pueden aiiadir todas sus clases basicas a1 cuadro de lista List Parent mediante el codigo siguiente: with ListParent.Items do begin Clear; while MyClass.ClassParent <> nil do begin MyClass := MyClass.ClassParent; Add (MyClass .ClassName) ; end; end ; Se usa una referencia de clase en la parte principal del bucle while, que comprueba la ausencia de una clase padre (de mod0 que la clase actual es TOb j ect). Como alternativa, podiamos haber escrito la sentencia while de una de las siguientes formas: while not MyClass.ClassNameIs ('TObject') d o ... while MyClass <> TObject do... El codigo de la sentencia with que se refiere a la lista Listparent es parte del ejemplo class Inf o, que muestra la lista de clases padres y alguna otra informacion sobre una serie de componentes de la VCL (basicamente aquellos de la pagina Standard de la Component Palette). Dichos componentes se aiiaden de forma manual a la matriz dinamica que mantiene las clases y que se declara como: private ClassArray: array of TClass; Cuando se inicia el programa, se usa la matriz para mostrar todos 10s nombres de clase en un cuadro de lista. A1 seleccionar un elemento del cuadro de lista se desencadena la presentacion visual de sus datos y sus clases basicas. - 7 N6%?& Comq extension adicional a este ejemplo. es posible crear un arb01 con todas las tlases basicas de diversos componentes en una jerarquia. i La biblioteca de clases principales Ya vimos que Delphi incluye una gran cantidad de funciones y procedimien- tos, per0 la autentica potencia de la programacion visual en Delphi reside en la gigantesca biblioteca de clases que proporciona. La biblioteca de clases estandar de Delphi contiene cientos de clases, con miles de metodos, y es tan inmensa que no se puede proporcionar una referencia detalla- da en este libro. En su lugar, exploraremos diversas areas de esta biblioteca a partir de este punto. Este capitulo esta dedicado a las clases principales de la biblioteca a1 igual que a algunas tecnicas estandar de programacion, como la definicion de eventos. Ex- ploraremos las clases mas habitualmente utilizadas, como las listas, listas de cadenas, colecciones y streams o flujos. La mayor parte del tiempo exploraremos 10s contenidos de la unidad c 1 a s se s, per0 tambien examinaremos otras unida- des principales de la biblioteca. Las clases de Delphi pueden utilizarse completamente desde el codigo o desde el diseiiador visual de formularios. Algunas de ellas son clases componentes, que apareceran en la paleta de componentes, y otras son de proposito mas general. Los terminos clase y componente puede usarse casi como sinonimos en Delphi. Los componentes son 10s elementos centrales de las aplicaciones Delphi. Cuando se escribe un programa, basicamente se escoge un cierto numero de componentes y se definen sus interacciones, y ya esta. Antes de comenzar con este capitulo, seria necesario disponer de una buena compresion del lenguaje, en temas como la herencia, las propiedades, 10s metodos virtuales, las referencias de clases y demas. Este capitulo trata 10s siguientes temas: El paquete RTL, CLX y VCL. TPersistent y published. La clase basica TComponent y sus propiedades Componentes y propiedad Eventos. Listas, clases contenedoras y colecciones. Streaming . Las unidades del paquete RTL. El paquete RTL, VCL y CLX Hasta la version 5, la biblioteca de clases de Delphi era conocida como VCL, que significa Biblioteca de Componentes Visuales (Visual Components Library). Se trata de una biblioteca de componentes que se proyecta sobre la API de Windows. Kylix, la version Delphi para Linux, introdujo una nueva biblioteca de compo- nentes, denominada CLX, pronunciado "clics", y que significa Biblioteca de Com- ponentes para Plataforma X o Multiplataforma (Component Library forX-Platform or Cross Platform). Delphi 6 fue la primera version en incluir ambas bibliotecas, la VCL y la CLX. Para 10s componentes visuales, las dos bibliotecas resultan alternativas. Sin embargo, las clases principales y las partes de la base de datos e Internet de las dos bibliotecas son basicamente compartidas. La VCL estaba considerada como una gran biblioteca unica, aunque 10s pro- gramadores solian referirse a diferentes partes de ella (componentes, controles, componentes no visuales, conjuntos de datos, controles data-aware, componentes de Internet, etc). CLX presenta una division en cuatro partes: BaseCLX, VisualCLX, DataCLX y NetCLX. La biblioteca utiliza un enfoque totalmente diferente entre Windows y Linux solo en VisualCLX, puesto que el resto del codigo puede transportarse de forma inherente a Linux. En las versiones mas recientes de Delphi, esta distincion se ve resaltada por el hecho de que 10s componentes y las clases centrales no visuales de la biblioteca forman parte del nuevo paquete RTL, que utilizan tanto la VCL como la CLX. Aun mas, utilizar este paquete en aplicaciones no visuales (por ejemplo, en pro- gramas de servidor Web) permite reducir considerablemente el tamaiio de 10s archivos que se van a desplegar y cargar en memoria. Partes tradicionales de la VCL Los programadores de Delphi se solian referir a distintas partes de la VCL con 10s nombres que Borland sugirio originalmente en su documentacion y que se hicieron comunes posteriormente para diferentes grupos de componentes. Tecni- camente, 10s componentes son subclases de la clase TComponent, que es una de las clases raiz de la jerarquia, como muestra la figura 4.1. En realidad, la clase TComponent hereda de la clase TPersistent. ventana Controles (subclases de (oomponentes visuales) TWinControl) Controles no de ventana (TComponent) (subclases de TGraphicControl)I Componentes no visuales I (otras subclases de TComponent) Figura 4.1. Una representacion grafica de 10s principales grupos de componentes de la VCL. Ademas de 10s componentes, la biblioteca incluye clases que heredan directa- mente de TOb j ect j1 de TPers is tent. Estas clases se conocen de mod0 colectivo como Objects en parte de la documentacion, un nombre bastante confu- so. Estas clases no componentes se utilizan normalmente para valores de propie- dades o como clases de utilidad empleadas en el c6digo; a1 no heredar de TComponent , no se pueden utilizar directamente en programacion visual. -- -- - NOTA: Para ser mb precisos, las clases no componentes no pueden estar disponibles en la Component Palette ni se pueden dejar eaer ditectamente en un formulario, pero se pueden, administtar visu.almeste con el Object Inspector, como subpropiedades de otras propiedadis o elemmtos de varios tipos. Por lo que, incluso las clases no componentes son n o ~ a h m t e faci- les de usar, gracias a la interfaz con el Form Designer. . Las clases componentes pueden dividirse ademas en dos grupos principales: controles y componentes no visuales. Controles: Todas las clases que descienden de TControl. Tienen una posicion y tamafio en pantalla y aparecen en el formulario en tiempo de diseiio en la misma posicion que tendrian en tiempo de ejecucion. Los controles tienen dos subespecificaciones diferentes, basados en ventanas o graficos. Componentes no visuales: Son todos 10s componentes que no son contro- les, todas las clases que descienden de T C o m p o n e n t pero no de T C o n t r o l . En tiempo de diseiio, un componente no visual aparece en el formulario o modulo de datos como un icono (con un titulo debajo opcional en 10s formularios). En tiempo de ejecucion, algunos de estos componentes pueden resultar visibles (por ejemplo, 10s cuadros de dialogo estandar) y otros estan visibles siempre (por ejemplo, el componente de tabla de la base de datos). 01 o componente en el Form Designer, se puede ver una sugerencia sobre herramientas con su nombre y tip0 de clase (y alguna information ampliada). Se puede utilizar tambien una opcion del entorno, show Component Captions, parai vet el nombre del componente no visual bajo su icono. Esta es la subdivision tradicional de VCL, muy comun para 10s programadores Delphi. A pesar de la introduccion de CLX y de algunas estructuras de denomina- cion nuevas, 10s nombres tradicionales sobreviviran probablemente y sc mezcla- ran en la jerga de 10s programadores en Delphi. La estructura de CLX Borland se refiere ahora a distintas secciones de la biblioteca CLX empleando una terminologia para Linux y una estructura de nombrado ligeramente distinta (y menos clara) en Delphi. Esta nueva subdivision de la biblioteca multiplataforma representa areas mas logicas que la estructura de la jerarquia de clases: BaseCLX: Forma el nucleo principal de la biblioteca de clases: las clases mas altas (corno TComponen t ) y diversas clases de utilidades generales (corno listas, contenedores, colecciones y streams). En comparacion con las clases correspondientes de la VCL, BaseCLX ha cambiado poco y resulta muy facil de transportar entre las plataformas Windows y Linux. Este capitulo se dedica en gran medida a explorar BaseCLS y las clases principales comunes de VCL. VisualCLX: Es la coleccion de componentes visuales, por lo general lla- mados controles. Esta es la parte de la biblioteca que esta relacionada mas estrechamente con el sistema operativo: VisualCLX se implementa en la parte superior de la biblioteca Qt, disponible tanto en Windows como en Linux. Utilizar VisualCLX permite una capacidad de transporte total de la parte visual de una aplicacion entre Delphi en Windows y Kylix en Linux. Sin embargo, la mayoria dc 10s componentes VisualCLX poscen sus co- rrespondientes controles VCL, por lo que podemos adaptar facilmente el codigo de una biblioteca a otra. DataCLX: Engloba todos 10s componentes relacionados con bases de da- tos de la biblioteca. En realidad, DataCLX es la fachada del nuevo motor de base de datos dbExpress incluido tanto en Delphi como Kylix. Delphi incluye tambien la tradicional interfaz BDE, dbGo e InterBase Express (IBX). Si consideramos todos estos componentes como parte de DataCLX, solo la interfaz dbExpress e IBX resultan transportables entre Windows y Linux. DataCLX incluye tambien el componente C l i e n t Data S e t, aho- ra llamado MyBa s e y otras clases relacionadas. NetCLX: Incluye 10s componentes relacionados con Internet, desde el marco de trabajo WebBroker, a 10s componentes del productor HTML, desde Indy (Internet Direct) a Internet Express, de WebSnap a1 soporte XML. Esta parte de la biblioteca es, una vez mas, muy facil de transportar entre Windows y Linux. Partes especificas de VCL de la biblioteca Las anteriores partes de la biblioteca estan disponibles, con las diferencias mencionadas, tanto en Delphi como en Kylix. Sin embargo, en Delphi existen otras secciones de la VCL, que por una razon u otra son solo especificas para Windows: El marco de trabajo Delphi ActiveX (DAX) proporciona soporte para COM, Automatizacion OLE, ActiveX y otras tecnologias relacionadas con COM. Los componentes Decision Cube ofrecen soporte OLAP, per0 tienen lazos con el BDE y no se han actualizado recientemente. No comentaremos estos componentes en el libro. Por ultimo, la instalacion predefinida de Delphi incluye algunos componentes creados por terceros, como el TeeChart para graficos empresariales, RAVE para generacion de informes e impresion e IntraWeb para desarrollo para Internet. Algunos de estos componentes se comentaran en el libro, per0 no forman estricta- mente parte de la VCL. RAVE e IntraWeb tambien se encuentran disponibles para Kylix. La clase TPersistent La primera clase principal de la biblioteca de Delphi que veremos es su clase T Pe r s i s t e n t , que es bastante atipica: tiene poco codigo y casi no tiene uso directo, per0 ofrece la base para la idea global de la programacion visual. Se puede ver la definicion de la clase en el listado 4.1. Listado 4.1. La definicion de la clase TPersistent, desde la unidad Classes. I S M + ) TPersistent = class (TObject) private procedure AssignError(Source: TPersistent); protected procedure AssignTo (Dest: TPersistent) ; virtual; procedure Defineproperties (Filer: TFiler) ; virtual; function Getowner: TPersistent; dynamic; public destructor Destroy; override; procedure Assign (Source: TPersistent) ; virtual; function GetNamePath: string; dynamic; end; Como su nombre indica, esta clase controla la permanencia (es decir, el hecho de guardar el valor de un objeto en un archivo para usarlo mas tarde y volver a crear el objeto en el mismo estado y con 10s mismos datos). La permanencia es un elemento clave de la programacion visual. De hecho, en tiempo de diseiio en Delphi manipulamos objetos reales, que se guardan en archivos DFM y se vuel- ven a crear en tiempo de ejecucion a1 mismo tiempo que el contenedor especifico (formulario o modulo de datos) del componente. El formato es idCntico. La diferencia en la exte Delphi la utiliza para detenninar si el formula Vf'T AUinrlnrtre En U x r l i v tnrln Fnrmn.lnAm mm - - - - - - - - - . .- NOTA: ~ ~ d o 1 re aplica tarnbien a 10s archivos XF M, el ~ o m r o ae arcolvo que u u ~ m ras t@xwiones CLX. ns&n ts 2@Mante porque rib se h a en CLS/Qt o en . v u r .. rrruwrru. uu x r j u n , wuv r w l u r u x u l v vu ~n f~rmulfwrip CLXIQt, sin importar la extension que se emplee; por eso, la extensi~n XF'IWDFM no tiene importancia en Kylix. Sin embargo, el soporte de streaming no es ta incluido en la clase TPersistent, aunque nos lo ofrecen otra clases, que tienen como objetivo T P e r s i s t ent y sus descendientes. En otras palabras, con el streaming predefinido de Delphi se puede hacer que