{"id":299,"date":"2023-04-17T08:46:00","date_gmt":"2023-04-17T08:46:00","guid":{"rendered":"https:\/\/jorgeturiel.es\/?p=299"},"modified":"2023-08-11T21:10:28","modified_gmt":"2023-08-11T20:10:28","slug":"introduccion-al-lenguaje-pascal-moderno-para-programadores-parte-3","status":"publish","type":"post","link":"https:\/\/jorgeturiel.es\/?p=299","title":{"rendered":"Introducci\u00f3n al lenguaje Pascal moderno para programadores (Parte 3)"},"content":{"rendered":"\n<p id=\"block-5521090e-ab25-4c7d-8ff9-7b06a21f4a1b\">Continuamos con la introducci\u00f3n al lenguaje Pascal Moderno.<\/p>\n\n\n\n<p id=\"block-404f08e6-e309-4eb0-84cf-cdf990136039\">Esta entrada es una traducci\u00f3n al espa\u00f1ol, del texto <a rel=\"noreferrer noopener\" href=\"https:\/\/castle-engine.io\/modern_pascal\" target=\"_blank\">original<\/a> escrito por Michalis Kamburelis.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h1 class=\"wp-block-heading\">Clases<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Lo esencial<\/h2>\n\n\n\n<p>En el nivel b\u00e1sico, una clase es solo un contenedor para:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>campos (que es un nombre elegante para \u00abuna variable dentro de una clase\u00bb)<\/li>\n\n\n\n<li>m\u00e9todos (que es un nombre elegante para \u00abun procedimiento o funci\u00f3n dentro de una clase\u00bb)<\/li>\n\n\n\n<li>Propiedades (que es una sintaxis elegante para algo que parece un campo, pero en realidad es un par de m\u00e9todos para obtener y establecer algo; m\u00e1s en Propiedades)<\/li>\n<\/ul>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:delphi decode:true \">type\n  TMyClass = class\n    MyInt: Integer; \/\/ this is a field\n    property MyIntProperty: Integer read MyInt write MyInt; \/\/ this is a property\n    procedure MyMethod; \/\/ this is a method\n  end;\n\nprocedure TMyClass.MyMethod;\nbegin\n  WriteLn(MyInt + 10);\nend;<\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">La herencia<\/h2>\n\n\n\n<p>Tenemos herencia y m\u00e9todos virtuales.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:delphi decode:true \">{$mode objfpc}{$H+}{$J-}\nprogram MyProgram;\n\nuses\n  SysUtils;\n\ntype\n  TMyClass = class\n    MyInt: Integer;\n    procedure MyVirtualMethod; virtual;\n  end;\n\n  TMyClassDescendant = class(TMyClass)\n    procedure MyVirtualMethod; override;\n  end;\n\nprocedure TMyClass.MyVirtualMethod;\nbegin\n  WriteLn('TMyClass shows MyInt + 10: ', MyInt + 10);\nend;\n\nprocedure TMyClassDescendant.MyVirtualMethod;\nbegin\n  WriteLn('TMyClassDescendant shows MyInt + 20: ', MyInt + 20);\nend;\n\nvar\n  C: TMyClass;\nbegin\n  C := TMyClass.Create;\n  try\n    C.MyVirtualMethod;\n  finally\n    FreeAndNil(C);\n  end;\n\n  C := TMyClassDescendant.Create;\n  try\n    C.MyVirtualMethod;\n  finally\n    FreeAndNil(C);\n  end;\nend.<\/pre><\/div>\n\n\n\n<p>Por defecto los m\u00e9todos no son virtuales, decl\u00e1ralos con virtual para hacerlos. Las anulaciones deben estar marcadas con <em>override<\/em>, de lo contrario recibir\u00e1 una advertencia. Para ocultar un m\u00e9todo sin anularlo (por lo general, no desea hacer esto, a menos que sepa lo que est\u00e1 haciendo), use <code><em>reintroduce<\/em><\/code>.<\/p>\n\n\n\n<p>Para probar la clase de una instancia en tiempo de ejecuci\u00f3n, use el operador <em>is<\/em>. Para encasillar la instancia en una clase espec\u00edfica, use el operador <em>as<\/em>.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \">{$mode objfpc}{$H+}{$J-}\nprogram is_as;\n\nuses\n  SysUtils;\n\ntype\n  TMyClass = class\n    procedure MyMethod;\n  end;\n\n  TMyClassDescendant = class(TMyClass)\n    procedure MyMethodInDescendant;\n  end;\n\nprocedure TMyClass.MyMethod;\nbegin\n  WriteLn('MyMethod');\nend;\n\nprocedure TMyClassDescendant.MyMethodInDescendant;\nbegin\n  WriteLn('MyMethodInDescendant');\nend;\n\nvar\n  Descendant: TMyClassDescendant;\n  C: TMyClass;\nbegin\n  Descendant := TMyClassDescendant.Create;\n  try\n    Descendant.MyMethod;\n    Descendant.MyMethodInDescendant;\n\n    { Descendant has all functionality expected of\n      the TMyClass, so this assignment is OK }\n    C := Descendant;\n    C.MyMethod;\n\n    { this cannot work, since TMyClass doesn't define this method }\n    \/\/C.MyMethodInDescendant;\n    if C is TMyClassDescendant then\n      (C as TMyClassDescendant).MyMethodInDescendant;\n\n  finally\n    FreeAndNil(Descendant);\n  end;\nend.<\/pre><\/div>\n\n\n\n<p>En lugar de usar X como <em>TMyClass<\/em>, tambi\u00e9n puede usar <em>TMyClass(X) <\/em>encasillado sin marcar. Esto es m\u00e1s r\u00e1pido, pero da como resultado un comportamiento indefinido si la X no es, de hecho, un descendiente de <em>TMyClass<\/em>. Por lo tanto, no use el tipo de conversi\u00f3n <em>TMyClass(X)<\/em>, o util\u00edcelo solo en un c\u00f3digo donde es absolutamente obvio que es correcto, por ejemplo, justo despu\u00e9s de probar con <em>is<\/em>:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:delphi decode:true \">if A is TMyClass then\n  (A as TMyClass).CallSomeMethodOfMyClass;\n\/\/ below is marginally faster\nif A is TMyClass then\n    TMyClass(A).CallSomeMethodOfMyClass;<\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Propiedades<\/h2>\n\n\n\n<p>Las propiedades son un muy buen \u00abaz\u00facar de sintaxis\u00bb para:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Cree algo que parezca un campo (que se pueda leer y configurar) pero que debajo se realice llamando a m\u00e9todos getter y setter. El uso t\u00edpico es realizar alg\u00fan efecto secundario (por ejemplo, volver a dibujar la pantalla) cada vez que cambia alg\u00fan valo<\/li>\n\n\n\n<li>Cree algo que parezca un campo, pero que sea de solo lectura. En efecto, es como una funci\u00f3n constante o sin par\u00e1metros<\/li>\n<\/ol>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:delphi decode:true \">type\n  TWebPage = class\n  private\n    FURL: string;\n    FColor: TColor;\n    function SetColor(const Value: TColor);\n  public\n    { No way to set it directly.\n      Call the Load method, like Load('http:\/\/www.freepascal.org\/'),\n      to load a page and set this property. }\n    property URL: string read FURL;\n    procedure Load(const AnURL: string);\n    property Color: TColor read FColor write SetColor;\n  end;\n\nprocedure TWebPage.Load(const AnURL: string);\nbegin\n  FURL := AnURL;\n  NetworkingComponent.LoadWebPage(AnURL);\nend;\n\nfunction TWebPage.SetColor(const Value: TColor);\nbegin\n  if FColor &lt;&gt; Value then\n  begin\n    FColor := Value;\n    \/\/ for example, cause some update each time value changes\n    Repaint;\n    \/\/ as another example, make sure that some underlying instance,\n    \/\/ like a \"RenderingComponent\" (whatever that is),\n    \/\/ has a synchronized value of Color.\n    RenderingComponent.Color := Value;\n  end;\nend;<\/pre><\/div>\n\n\n\n<p>Tenga en cuenta que en lugar de especificar un m\u00e9todo, tambi\u00e9n puede especificar un campo (normalmente un campo privado) para obtener o establecer directamente. En el ejemplo anterior, la propiedad Color utiliza un m\u00e9todo establecido SetColor. Pero para obtener el valor, la propiedad Color se refiere directamente al campo privado FColor. Hacer referencia directa a un campo es m\u00e1s r\u00e1pido que implementar m\u00e9todos getter o setter triviales (m\u00e1s r\u00e1pido para usted y m\u00e1s r\u00e1pido en la ejecuci\u00f3n).<\/p>\n\n\n\n<p>Al declarar una propiedad se especifica:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Si se puede leer y c\u00f3mo (leyendo directamente un campo o usando un m\u00e9todo \u00abcaptador\u00bb).<\/li>\n\n\n\n<li>Y, de manera similar, si se puede establecer y c\u00f3mo (escribiendo directamente en un campo designado o llamando a un m\u00e9todo de \u00absetter\u00bb).<\/li>\n<\/ol>\n\n\n\n<p>El compilador verifica que los tipos y par\u00e1metros de los campos y m\u00e9todos indicados coincidan con el tipo de propiedad. Por ejemplo, para leer una propiedad de n\u00famero entero, debe proporcionar un campo de n\u00famero entero o un m\u00e9todo sin par\u00e1metros que devuelva un n\u00famero entero.<\/p>\n\n\n\n<p>T\u00e9cnicamente, para el compilador, los m\u00e9todos \u00abgetter\u00bb y \u00absetter\u00bb son simplemente m\u00e9todos normales y pueden hacer absolutamente cualquier cosa (incluidos los efectos secundarios o la aleatorizaci\u00f3n). Pero es una buena convenci\u00f3n dise\u00f1ar propiedades para que se comporten m\u00e1s o menos como campos:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>La funci\u00f3n getter no deber\u00eda tener efectos secundarios visibles (por ejemplo, no deber\u00eda leer alguna entrada del archivo\/teclado). Debe ser determinista (sin aleatorizaci\u00f3n, ni siquiera pseudoaleatorizaci\u00f3n :). Leer una propiedad muchas veces deber\u00eda ser v\u00e1lido y devolver el mismo valor, si nada cambi\u00f3 en el medio.<\/li>\n<\/ul>\n\n\n\n<p>Tenga en cuenta que est\u00e1 bien que getter tenga alg\u00fan efecto secundario invisible, por ejemplo, almacenar en cach\u00e9 un valor de alg\u00fan c\u00e1lculo (conocido por producir los mismos resultados para una instancia determinada), para devolverlo m\u00e1s r\u00e1pido la pr\u00f3xima vez. <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>De hecho, esta es una de las interesantes posibilidades de una funci\u00f3n \u00abcaptador\u00bb. La funci\u00f3n setter siempre debe establecer el valor solicitado, de modo que llamar al getter lo devuelva. No rechace valores inv\u00e1lidos en silencio en el \u00absetter\u00bb (provoque una excepci\u00f3n si es necesario). No convierta ni escale el valor solicitado. La idea es que despu\u00e9s de MyClass.MyProperty := 123; el programador puede esperar que MyClass.MyProperty = 123.<\/li>\n\n\n\n<li>Las propiedades de solo lectura a menudo se usan para hacer que algunos campos sean de solo lectura desde el exterior. Nuevamente, la buena convenci\u00f3n es hacer que se comporte como una constante, al menos constante para esta instancia de objeto con este estado. El valor de la propiedad no debe cambiar inesperadamente. Convi\u00e9rtalo en una funci\u00f3n, no en una propiedad, si su uso tiene un efecto secundario o devuelve algo aleatorio.<\/li>\n\n\n\n<li>El campo de \u00abrespaldo\u00bb de una propiedad es casi siempre privado, ya que la idea de una propiedad es encapsular todo acceso externo a ella.<\/li>\n\n\n\n<li>Es t\u00e9cnicamente posible crear propiedades de solo conjunto, pero a\u00fan no he visto un buen ejemplo de tal cosa \ud83d\ude42<\/li>\n<\/ul>\n\n\n\n<p>Las propiedades tambi\u00e9n se pueden definir fuera de la clase, a nivel de unidad. Entonces, tienen un prop\u00f3sito an\u00e1logo: parecen una variable global, pero est\u00e1n respaldados por rutinas getter y setter.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Serializacion de propiedades <\/h3>\n\n\n\n<p>Las propiedades publicadas son la base de una serializaci\u00f3n (tambi\u00e9n conocida como componentes de transmisi\u00f3n) en Pascal. La serializaci\u00f3n significa que los datos de la instancia se registran en un flujo (como un archivo), desde el cual se pueden restaurar m\u00e1s tarde.<\/p>\n\n\n\n<p>La serializaci\u00f3n es lo que sucede cuando Lazarus lee (o escribe) el estado del componente desde un archivo xxx.lfm. (En Delphi, el archivo equivalente tiene la extensi\u00f3n .dfm). Tambi\u00e9n puede usar este mecanismo expl\u00edcitamente, usando rutinas como <em>ReadComponentFromTextStream<\/em> de la unidad <em>LResources<\/em>. Tambi\u00e9n puede usar otros algoritmos de serializaci\u00f3n, por ejemplo la  Unidad <em>FpJsonRtti<\/em> (serializar a JSON).<\/p>\n\n\n\n<p>En <strong>Castle Game Engine<\/strong>: use la unidad <em>CastleComponentSerialize<\/em> (basada en <em>FpJsonRtti<\/em>) para serializar nuestras jerarqu\u00edas de interfaz de usuario y componentes de transformaci\u00f3n.<\/p>\n\n\n\n<p>En cada propiedad, puede declarar algunas cosas adicionales que ser\u00e1n \u00fatiles para cualquier algoritmo de serializaci\u00f3n:<\/p>\n\n\n\n<p>Puede especificar el valor predeterminado de la propiedad (usando la palabra clave predeterminada). <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Tenga en cuenta que a\u00fan debe inicializar la propiedad en el constructor a este valor predeterminado exacto (no se hace autom\u00e1ticamente). La declaraci\u00f3n predeterminada es simplemente una informaci\u00f3n para el algoritmo de serializaci\u00f3n: \u00abcuando finaliza el constructor, la propiedad dada tiene el valor dado\u00bb.<\/li>\n\n\n\n<li>Si la propiedad debe almacenarse en absoluto (usando la palabra clave almacenada).<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Excepciones. Un ejemplo r\u00e1pido<\/h2>\n\n\n\n<p>Tenemos excepciones. Se pueden atrapar con cl\u00e1usulas try\u2026except\u2026\u200b end, y finalmente tenemos secciones como try\u2026finally\u2026end.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:delphi decode:true \">$mode objfpc}{$H+}{$J-}\n\nprogram MyProgram;\n\nuses\n  SysUtils;\n\ntype\n  TMyClass = class\n    procedure MyMethod;\n  end;\n\nprocedure TMyClass.MyMethod;\nbegin\n  if Random &gt; 0.5 then\n    raise Exception.Create('Raising an exception!');\nend;\n\nvar\n  C: TMyClass;\nbegin\n  Randomize;\n  C := TMyClass.Create;\n  try\n    C.MyMethod;\n  finally\n    FreeAndNil(C);\n  end;\nend.<\/pre><\/div>\n\n\n\n<p>Tenga en cuenta que la cl\u00e1usula finalmente se ejecuta incluso si sale del bloque usando Exit (desde la funci\u00f3n\/procedimiento\/m\u00e9todo) o Break o Continue (desde el cuerpo del bucle). <\/p>\n\n\n\n<p>Consulte el cap\u00edtulo Excepciones para obtener una descripci\u00f3n m\u00e1s detallada de las excepciones.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Especificadores de visibilidad<\/h2>\n\n\n\n<p>Como en la mayor\u00eda de los lenguajes orientados a objetos, tenemos especificadores de visibilidad para ocultar campos\/m\u00e9todos\/propiedades.<\/p>\n\n\n\n<p>Los niveles b\u00e1sicos de visibilidad son:<\/p>\n\n\n\n<p><em>Public<\/em><\/p>\n\n\n\n<p>todos pueden acceder a \u00e9l, incluido el c\u00f3digo en otras unidades.<\/p>\n\n\n\n<p>Private<\/p>\n\n\n\n<p>s\u00f3lo accesible en esta clase.<\/p>\n\n\n\n<p>Protected<\/p>\n\n\n\n<p> solo accesible en esta clase y descendientes.<\/p>\n\n\n\n<p>La explicaci\u00f3n anterior de visibilidad privada y protegida no es precisamente cierta. El c\u00f3digo en la misma unidad puede superar sus l\u00edmites y acceder libremente a las cosas privadas y protegidas. A veces, esta es una buena caracter\u00edstica, le permite implementar clases estrechamente conectadas. Use estricta privacidad o estricta protecci\u00f3n para asegurar sus clases de manera m\u00e1s estricta. Ver el privado y privado estricto.<\/p>\n\n\n\n<p>De forma predeterminada, si no especifica la visibilidad, la visibilidad de las cosas declaradas es p\u00fablica. La excepci\u00f3n son las clases compiladas con {$M+} o los descendientes de las clases compiladas con {$M+}, que incluye todos los descendientes de TPersistent, que tambi\u00e9n incluye todos los descendientes de TComponent (ya que TComponent desciende de TPersistent). Para ellos, se publica el especificador de visibilidad predeterminado, que es como p\u00fablico, pero adem\u00e1s el sistema de transmisi\u00f3n sabe manejar esto.<\/p>\n\n\n\n<p>No todos los campos y tipos de propiedades est\u00e1n permitidos en la secci\u00f3n publicada (no todos los tipos se pueden transmitir y solo las clases se pueden transmitir desde campos simples). Solo use p\u00fablico si no le importa la transmisi\u00f3n pero quiere algo disponible para todos los usuarios.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Antecesor por defecto<\/h2>\n\n\n\n<p>Si no declara el tipo antepasado, cada clase hereda de TObject.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Self<\/h2>\n\n\n\n<p>La palabra clave especial Self se puede usar dentro de la implementaci\u00f3n de la clase para referirse expl\u00edcitamente a su propia instancia. Es equivalente a esto de C++, Java y lenguajes similares.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Llamando al m\u00e9todo heredado<\/h2>\n\n\n\n<p>Dentro de la implementaci\u00f3n de un m\u00e9todo, si llama a otro m\u00e9todo, por defecto llama al m\u00e9todo de su propia clase. En el c\u00f3digo de ejemplo a continuaci\u00f3n, TMyClass2.MyOtherMethod llama a MyMethod, que termina llamando a TMyClass2.MyMethod.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:delphi decode:true \">{$mode objfpc}{$H+}{$J-}\nuses SysUtils;\n\ntype\n  TMyClass1 = class\n    procedure MyMethod;\n  end;\n\n  TMyClass2 = class(TMyClass1)\n    procedure MyMethod;\n    procedure MyOtherMethod;\n  end;\n\nprocedure TMyClass1.MyMethod;\nbegin\n  Writeln('TMyClass1.MyMethod');\nend;\n\nprocedure TMyClass2.MyMethod;\nbegin\n  Writeln('TMyClass2.MyMethod');\nend;\n\nprocedure TMyClass2.MyOtherMethod;\nbegin\n  MyMethod; \/\/ this calls TMyClass2.MyMethod\nend;\n\nvar\n  C: TMyClass2;\nbegin\n  C := TMyClass2.Create;\n  try\n    C.MyOtherMethod;\n  finally FreeAndNil(C) end;\nend.<\/pre><\/div>\n\n\n\n<p>Si el m\u00e9todo no est\u00e1 definido en una clase determinada, llama al m\u00e9todo de una clase antepasada. En efecto, cuando llama a MyMethod en una instancia de TMyClass2, entonces:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>El compilador busca TMyClass2.MyMethod<\/li>\n\n\n\n<li>Si no lo encuentra, busca TMyClass1.MyMethod.<\/li>\n\n\n\n<li>Si no lo encuentra, busca TObject.MyMethod.<\/li>\n\n\n\n<li>si no se encuentra, la compilaci\u00f3n falla. <\/li>\n<\/ul>\n\n\n\n<p>Puede probarlo comentando la definici\u00f3n de TMyClass2.MyMethod en el ejemplo anterior. En efecto, TMyClass1.MyMethod ser\u00e1 llamado por TMyClass2.MyOtherMethod.<\/p>\n\n\n\n<p>A veces, no desea llamar al m\u00e9todo de su propia clase. Desea llamar al m\u00e9todo de un antepasado (o al antepasado de un antepasado, etc.). Para hacer esto, agregue la palabra clave heredada antes de la llamada a MyMethod, as\u00ed:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:delphi decode:true \">inherited MyMethod;<\/pre><\/div>\n\n\n\n<p>De esta forma, fuerza al compilador a comenzar a buscar desde una clase antecesora. En nuestro ejemplo, significa que el compilador est\u00e1 buscando MyMethod dentro de TMyClass1.MyMethod, luego TObject.MyMethod y luego se da por vencido. Ni siquiera considera usar la implementaci\u00f3n de TMyClass2.MyMethod.<\/p>\n\n\n\n<p>TMyClass2.MyOtherMethod above to use inherited MyMethod, and see the difference in the output.139 \/ 5.000<\/p>\n\n\n\n<p>Consejo:  a delante, cambie la implementaci\u00f3n de TMyClass2.MyOtherMethod anterior para usar MyMethod heredado y vea la diferencia en el resultado.<\/p>\n\n\n\n<p>La llamada heredada se usa a menudo para llamar al m\u00e9todo antecesor del mismo nombre. De esta forma, los descendientes pueden mejorar a los ancestros (manteniendo la funcionalidad del ancestro, en lugar de reemplazar la funcionalidad del ancestro). Como en el ejemplo de abajo.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:delphi decode:true \">{$mode objfpc}{$H+}{$J-}\nuses SysUtils;\n\ntype\n  TMyClass1 = class\n    constructor Create;\n    procedure MyMethod(const A: Integer);\n  end;\n\n  TMyClass2 = class(TMyClass1)\n    constructor Create;\n    procedure MyMethod(const A: Integer);\n  end;\n\nconstructor TMyClass1.Create;\nbegin\n  inherited Create; \/\/ this calls TObject.Create\n  Writeln('TMyClass1.Create');\nend;\n\nprocedure TMyClass1.MyMethod(const A: Integer);\nbegin\n  Writeln('TMyClass1.MyMethod ', A);\nend;\n\nconstructor TMyClass2.Create;\nbegin\n  inherited Create; \/\/ this calls TMyClass1.Create\n  Writeln('TMyClass2.Create');\nend;\n\nprocedure TMyClass2.MyMethod(const A: Integer);\nbegin\n  inherited MyMethod(A); \/\/ this calls TMyClass1.MyMethod\n  Writeln('TMyClass2.MyMethod ', A);\nend;\n\nvar\n  C: TMyClass2;\nbegin\n  C := TMyClass2.Create;\n  try\n    C.MyMethod(123);\n  finally FreeAndNil(C) end;\nend.<\/pre><\/div>\n\n\n\n<p>Dado que el uso <code><em>inherited<\/em><\/code> para llamar a un m\u00e9todo con el mismo nombre, con los mismos argumentos, es un caso muy frecuente, hay un atajo especial para ello: puede escribir heredado; (palabra clave heredada seguida inmediatamente por un punto y coma, en lugar de un nombre de m\u00e9todo). Esto significa \u00abllamar a un m\u00e9todo heredado con el mismo nombre, pas\u00e1ndole los mismos argumentos que el m\u00e9todo actual\u00bb.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Consejo: En el ejemplo anterior, todos los <code><em>inherited<\/em><\/code>\u2026\u200b; las llamadas podr\u00edan ser reemplazadas por un simple <code><em>inherited<\/em><\/code>.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:delphi decode:true \">procedure TMyClass2.MyMethod(A: Integer);\nbegin\n  Writeln('TMyClass2.MyMethod beginning ', A);\n  A := 456;\n  { This calls TMyClass1.MyMethod with A = 456,\n    regardless of the A value passed to this method (TMyClass2.MyMethod). }\n  inherited;\n  Writeln('TMyClass2.MyMethod ending ', A);\nend;<\/pre><\/div>\n\n\n\n<p>Nota 2: por lo general, desea que MyMethod sea virtual cuando muchas clases (a lo largo de la \u00abcadena de herencia\u00bb) lo definen. M\u00e1s sobre los m\u00e9todos virtuales en la secci\u00f3n a continuaci\u00f3n. Pero la palabra clave heredada funciona independientemente de si el m\u00e9todo es virtual o no. El heredado siempre significa que el compilador comienza a buscar el m\u00e9todo en un ancestro, y tiene sentido tanto para los m\u00e9todos virtuales como para los no virtuales.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">M\u00e9todos virtuales, anular y reintroducir<\/h2>\n\n\n\n<p>Por defecto, los m\u00e9todos no son virtuales. Esto es similar a C++ y diferente a Java.<\/p>\n\n\n\n<p>Cuando un m\u00e9todo no es virtual, el compilador determina a qu\u00e9 m\u00e9todo llamar seg\u00fan el tipo de clase declarado actualmente, no seg\u00fan el tipo de clase realmente creado. La diferencia parece sutil, pero es importante cuando se declara que su variable tiene una clase como TFruit, pero de hecho puede ser una clase descendiente como TApple.<\/p>\n\n\n\n<p>La idea de la programaci\u00f3n orientada a objetos es que la clase descendiente siempre es tan buena como el antepasado, por lo que el compilador permite usar una clase descendiente siempre que se espera el antepasado. Cuando tu m\u00e9todo no es virtual, esto puede tener consecuencias no deseadas. Considere el siguiente ejemplo:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:delphi decode:true \">{$mode objfpc}{$H+}{$J-}\nuses SysUtils;\n\ntype\n  TFruit = class\n    procedure Eat;\n  end;\n\n  TApple = class(TFruit)\n    procedure Eat;\n  end;\n\nprocedure TFruit.Eat;\nbegin\n  Writeln('Eating a fruit');\nend;\n\nprocedure TApple.Eat;\nbegin\n  Writeln('Eating an apple');\nend;\n\nprocedure DoSomethingWithAFruit(const Fruit: TFruit);\nbegin\n  Writeln('We have a fruit with class ', Fruit.ClassName);\n  Writeln('We eat it:');\n  Fruit.Eat;\nend;\n\nvar\n  Apple: TApple; \/\/ Note: you could as well declare \"Apple: TFruit\" here\nbegin\n  Apple := TApple.Create;\n  try\n    DoSomethingWithAFruit(Apple);\n  finally FreeAndNil(Apple) end;\nend.<\/pre><\/div>\n\n\n\n<p>Este ejemplo mostrar\u00e1<\/p>\n\n\n\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained\">\n<p>We have a fruit with class TApple<\/p>\n\n\n\n<p> We eat it: <\/p>\n\n\n\n<p>Eating a fruit<\/p>\n\n\n\n<p>En efecto, la llamada Fruit.Eat llam\u00f3 a la implementaci\u00f3n TFruit.Eat y nada llam\u00f3 a la implementaci\u00f3n TApple.Eat. <\/p>\n\n\n\n<p>Si piensa en c\u00f3mo funciona el compilador, esto es natural: cuando escribi\u00f3 Fruit.Eat, se declar\u00f3 que la variable Fruit conten\u00eda una clase TFruit. Entonces, el compilador estaba buscando el m\u00e9todo llamado Eat dentro de la clase TFruit. Si la clase TFruit no contuviera dicho m\u00e9todo, el compilador buscar\u00eda dentro de un ancestro (TObject en este caso). Pero el compilador no puede buscar dentro de los descendientes (como TApple), ya que no sabe si la clase real de Fruit es TApple, TFruit o alg\u00fan otro descendiente de TFruit (como TOrange, que no se muestra en el ejemplo anterior).<\/p>\n\n\n\n<p>En otras palabras, el m\u00e9todo a llamar se determina en tiempo de compilaci\u00f3n.<\/p>\n\n\n\n<p>El uso de los m\u00e9todos virtuales cambia este comportamiento. Si el m\u00e9todo Eat fuera virtual (a continuaci\u00f3n se muestra un ejemplo), la implementaci\u00f3n real que se llamar\u00e1 se determina en tiempo de ejecuci\u00f3n. Si la variable Fruit contiene una instancia de la clase TAple (incluso si se declara como TFruit), el m\u00e9todo Eat se buscar\u00e1 primero dentro de la clase TAple. <\/p>\n\n\n\n<p>En Object Pascal, para definir un m\u00e9todo como virtual, necesita:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Marque su primera definici\u00f3n (en el ancestro m\u00e1s alto) con la palabra clave virtual.<\/li>\n\n\n\n<li>Marque todas las dem\u00e1s definiciones (en los descendientes) con la palabra clave override. Todas las versiones anuladas deben tener exactamente los mismos par\u00e1metros (y devolver los mismos tipos, en el caso de las funciones).<\/li>\n<\/ul>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:delphi decode:true \">{$mode objfpc}{$H+}{$J-}\nuses SysUtils;\n\ntype\n  TFruit = class\n    procedure Eat; virtual;\n  end;\n\n  TApple = class(TFruit)\n    procedure Eat; override;\n  end;\n\nprocedure TFruit.Eat;\nbegin\n  Writeln('Eating a fruit');\nend;\n\nprocedure TApple.Eat;\nbegin\n  Writeln('Eating an apple');\nend;\n\nprocedure DoSomethingWithAFruit(const Fruit: TFruit);\nbegin\n  Writeln('We have a fruit with class ', Fruit.ClassName);\n  Writeln('We eat it:');\n  Fruit.Eat;\nend;\n\nvar\n  Apple: TApple; \/\/ Note: you could as well declare \"Apple: TFruit\" here\nbegin\n  Apple := TApple.Create;\n  try\n    DoSomethingWithAFruit(Apple);\n  finally FreeAndNil(Apple) end;\nend.\n\n<\/pre><\/div>\n\n\n\n<p>Este ejemplo mostrar\u00e1<\/p>\n\n\n\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained\">\n<p>We have a fruit with class<\/p>\n\n\n\n<p> TApple We eat it: <\/p>\n\n\n\n<p>Eating an apple<\/p>\n\n\n\n<p>Internamente, los m\u00e9todos virtuales funcionan al tener la llamada tabla de m\u00e9todos virtuales asociada con cada clase. Esta tabla es una lista de punteros a las implementaciones de m\u00e9todos virtuales para esta clase. Al llamar al m\u00e9todo Eat, el compilador busca en una tabla de m\u00e9todo virtual asociada con la clase real de Fruit y usa un puntero a la implementaci\u00f3n de Eat almacenada all\u00ed.<\/p>\n\n\n\n<p>Si no usa la palabra clave override, el compilador le advertir\u00e1 que est\u00e1 ocultando (oscureciendo) el m\u00e9todo virtual de un ancestro con una definici\u00f3n no virtual. Si est\u00e1 seguro de que esto es lo que desea, puede agregar una palabra clave de reintroducci\u00f3n. Pero en la mayor\u00eda de los casos, preferir\u00e1 mantener el m\u00e9todo virtual y agregar la palabra clave anular, asegur\u00e1ndose as\u00ed de que siempre se invoque correctamente.<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-trasteando-por-vicio wp-block-embed-trasteando-por-vicio\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"oKzPf8gvAc\"><a href=\"https:\/\/jorgeturiel.es\/?p=312\">Introducci\u00f3n al lenguaje Pascal moderno para programadores (Parte 4)<\/a><\/blockquote><iframe loading=\"lazy\" class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; clip: rect(1px, 1px, 1px, 1px);\" title=\"\u00abIntroducci\u00f3n al lenguaje Pascal moderno para programadores (Parte 4)\u00bb \u2014 Trasteando por vicio\" src=\"https:\/\/jorgeturiel.es\/?p=312&#038;embed=true#?secret=iv7W2fa9S7#?secret=oKzPf8gvAc\" data-secret=\"oKzPf8gvAc\" width=\"600\" height=\"338\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe>\n<\/div><\/figure>\n<\/div><\/div>\n<\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Continuamos con la introducci\u00f3n al lenguaje Pascal Moderno. Esta entrada es una traducci\u00f3n al espa\u00f1ol, del texto original escrito por Michalis Kamburelis.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[27,25,26],"tags":[22,23,21,24],"class_list":["post-299","post","type-post","status-publish","format-standard","hentry","category-lazarus","category-pascal","category-programacion","tag-delphi","tag-lazarus","tag-pascal","tag-programacion"],"_links":{"self":[{"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/posts\/299","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=299"}],"version-history":[{"count":14,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/posts\/299\/revisions"}],"predecessor-version":[{"id":467,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=\/wp\/v2\/posts\/299\/revisions\/467"}],"wp:attachment":[{"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=299"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=299"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jorgeturiel.es\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=299"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}