Ir al contenido



Foto

¿String a Enumerativo con record helper? ¿Es posible?

enumerativo record helper string

  • Por favor identifícate para responder
17 respuestas en este tema

#1 Delphius

Delphius

    Advanced Member

  • Administrador
  • 5.956 mensajes
  • LocationArgentina

Escrito 10 mayo 2016 - 05:37

Buenas, tengo una duda curiosa...

 

No estoy muy familiarizado con estas nuevas características helpers y me asalta la duda. Trato de leer sobre el tema, y se que es posible hacer un record helper para un enumerativo, y gracias a éste implementar un método como ToString() o ToChar() para tener una representación "texto" del tipo en cuestión:


delphi
  1. TMiEnum = (meUno, meDos, ....);
  2.  
  3. TMiEnumHelper = record Helper for TMiEnum
  4. function ToChar: char;
  5. end;
  6.  
  7. function TMiEnumHelper.Tochar: char
  8. begin
  9. case Self of
  10. meUno: result := 'U';
  11. meDos: result := 'D';
  12. // ...
  13. end;

Entonces con esto es posible esta gracia:


delphi
  1. procedure MostrarTextoEnum;
  2. var MiEnum: TMiEnum;
  3. begin
  4. MiEnum := meUno;
  5. ShowMessage(MiEnum.ToChar);
  6. end;

Ahora bien. Si yo quisiera (y necesitara) hacer lo inverso, ¿Es posible? Es decir, dado un carácter, texto, o un número entero si se quiere, ¿Es posible obtener el tipo enumerativo correspondiente? ¿O necesariamente debe recurrirse a la vieja escuela?

 

No se, imaginaba algo como esto:


delphi
  1. MiEnum := TMiEnumHelper.ToEnum('U');

Pero como que parece algo raro... no encuentro una bibliografía que me aclare dudas. De si es "legal" esta vía o no.

 

Esto me lo pregunto porque justamente tengo unos tipos enumerativos y guardo en la base de datos una versión numerica o char de los mismos. Al momento de recuperar este valor me resultaría de interés materializar el tipo enumerativo.

Y justamente la idea es que ya que existen los helper pues... evitarme una función CharToEnum "suelta" a la vieja escuela.

 

Saludos,

 

 


  • 0

#2 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.220 mensajes
  • LocationRepública Dominicana

Escrito 10 mayo 2016 - 05:57

¿y no te sirve usar la Función toString() directamente?, no sé, algo como esto:


delphi
  1. TMiEnum = (meUno, meDos, ....);
  2.  
  3. TMiEnumHelper = record Helper for TMiEnum
  4. function ToChar: char;
  5. function ToString: String;
  6. end;
  7.  
  8. function TMiEnumHelper.Tochar: char
  9. begin
  10. case Self of
  11. meUno: result := 'U';
  12. meDos: result := 'D';
  13. // ...
  14. end;
  15.  
  16. function TMiEnumHelper.ToString: String;
  17. begin
  18. Result := ToString(Self);
  19. end;

Saludos.


  • 0

#3 Delphius

Delphius

    Advanced Member

  • Administrador
  • 5.956 mensajes
  • LocationArgentina

Escrito 10 mayo 2016 - 06:12

Pues para el primer paso quizá... tendré que ir probando. Lo que me interesa realmente es lo inverso.

Lo curioso es que aparentemente Lazarus no soporta helper sobre el enumerativo :(

 

Me arroja el error "Error record type expected".

Al menos eso da a entender... que se espera un record y no un enumerativo.

Se que con Delphi XE3+ es posible hacer esto sobre enumativos.Y encima hay que habilitar el modo:


delphi
  1. {$ModeSwitch advancedrecords}

Para que compile.

 

Saludos,


  • 0

#4 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.220 mensajes
  • LocationRepública Dominicana

Escrito 10 mayo 2016 - 06:23

¿Has intentado lo siguiente?, digo, no lo he probado pero puede funcionar:


delphi
  1. function TMiEnumHelper.ToEnum: TMiEnum;
  2. var c: char;
  3. begin
  4.  
  5. c := char(Self);
  6. case c of
  7. 'U': result := meUno;
  8. 'D': result := meDos;
  9. // ...
  10. end;

Saludos.


  • 0

#5 Delphius

Delphius

    Advanced Member

  • Administrador
  • 5.956 mensajes
  • LocationArgentina

Escrito 10 mayo 2016 - 07:05

Recién vuelvo de cenar.

No lo he probado, pero el error me advierte ya de entrada en que no acepta el enumerativo en su declaración, asi que no tendría sentido ni intentarlo. :(

 

La opción que si me deja es utilizar una indirección, es decir: definir un record y que tenga como campo el tipo enumerativo.

 

Entonces probé esto:


delphi
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$ModeSwitch advancedrecords}
  5.  
  6. interface
  7.  
  8. uses
  9. Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
  10.  
  11. type
  12. TMiEnum = (meUno, meDos, meTres);
  13. TMiRec = record
  14. Value: TMiEnum;
  15. end;
  16.  
  17. { TMiRecHelper }
  18.  
  19. TMiRecHelper = record helper for TMiRec
  20. function Tochar: char;
  21. function ToEnum(Value: char): TMiRec;
  22. end;
  23.  
  24. { TForm1 }
  25.  
  26. TForm1 = class(TForm)
  27. Button1: TButton;
  28. Button2: TButton;
  29. procedure Button1Click(Sender: TObject);
  30. procedure Button2Click(Sender: TObject);
  31. private
  32. { private declarations }
  33. public
  34. { public declarations }
  35. end;
  36.  
  37. var
  38. Form1: TForm1;
  39.  
  40. implementation
  41.  
  42. {$R *.lfm}
  43.  
  44. { TForm1 }
  45.  
  46. procedure TForm1.Button1Click(Sender: TObject);
  47. var r: TMiRec;
  48. begin
  49. r.Value := meUno;
  50. ShowMessage(r.Tochar);
  51. end;
  52.  
  53. procedure TForm1.Button2Click(Sender: TObject);
  54. var r: TmiRec;
  55. begin
  56. r := TMiRecHelper.ToEnum('2');
  57. ShowMessage(r.Tochar);
  58. end;
  59.  
  60. { TMiRecHelper }
  61.  
  62. function TMiRecHelper.ToChar: char;
  63. begin
  64. case self.Value of
  65. meUno: result := '1';
  66. meDos: result := '2';
  67. meTres: result := '3';
  68. end;
  69. end;
  70.  
  71. function TMiRecHelper.ToEnum(Value: char): TMiRec;
  72. var r: TMiRec;
  73. begin
  74. case Value of
  75. '1': r.Value := meUno;
  76. '2': r.Value := meDos;
  77. '3': r.Value := meTres;
  78. end;
  79. result := r;
  80. end;
  81.  
  82. end.

Y efectivamente ahora si no se queja del record esperado. El problema se traduce en que la expresión inversa inventada:


delphi
  1. r := TMiRecHelper.ToEnum('2');

Es ilegal. El error concreto es: "unit1.pas(56,20) Error: Objective-C categories and Object Pascal class helpers cannot be used as types"

 

Si lograse hacerlo andar con records helpers, me parece un camino un tanto engorroso. Si ya se está por emplear una vía indirecta, ese tipo record; para tal caso me evito el helper y hago mi propia función EnumToChar y CharToEnum de toda la vida. Si soportara el enumerativo bueno la podría pelear para buscar el proceso inverso.

Me siento un tanto decepcionado porque pensé que por fin podría hacer uso de las cosas nuevas que trae Object Pascal. Me bajó un poco los ánimos.

 

Saludos,


  • 0

#6 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 775 mensajes
  • LocationArgentina

Escrito 10 mayo 2016 - 09:01

Usa una funcion de clase :)

Pero antes, hay otro "truco" para que funcione en Lazarus
 
No podes implementar un record helper sobre un enumerativo como en Delphi
 
Tenes que usar un type helper
 
La verdad que en el area de helpers FPC es mil veces mejor a Delphi (lo mas notable es que soporta herencia)
 
---
 
El truco para que ande es el siguiente:

delphi
  1. {$ModeSwitch typehelpers}

Luego declaramos el Enumerativo y el Type Helper

delphi
  1. type
  2. TEnum = (enUno, enDos);
  3.  
  4. { TEnumHelper }
  5.  
  6. TEnumHelper = type helper for TEnum
  7. public
  8. class function Parse(const Value: string): TEnum; overload; static;
  9. class function Parse(const Value: Integer): TEnum; overload; static;
  10.  
  11. function ToInteger: Integer;
  12. class function ToString(const Value: TEnum): string; overload; static;
  13. function ToString: string; overload;
  14. end;

La implementacion es trivial..

delphi
  1. { TEnumHelper }
  2.  
  3. class function TEnumHelper.Parse(const Value: string): TEnum;
  4. var
  5. I: TEnum;
  6. begin
  7. for I := Low(TEnum) to High(TEnum) do
  8. begin
  9. if I.ToString = Value then
  10. Exit(I);
  11. end;
  12.  
  13. raise Exception.CreateFmt('TEnumHelper.Parse (string) Value %s', [Value]);
  14. end;
  15.  
  16. class function TEnumHelper.Parse(const Value: Integer): TEnum;
  17. var
  18. I: TEnum;
  19. begin
  20. for I := Low(TEnum) to High(TEnum) do
  21. begin
  22. if I.ToInteger = Value then
  23. Exit(I);
  24. end;
  25.  
  26. raise Exception.CreateFmt('TEnumHelper.Parse (Integer) Value %d', [Value]);
  27. end;
  28.  
  29. function TEnumHelper.ToInteger: Integer;
  30. begin
  31. Result := Ord(Self);
  32. end;
  33.  
  34. class function TEnumHelper.ToString(const Value: TEnum): string;
  35. begin
  36. case Value of
  37. enUno: Result := 'Uno';
  38. enDos: Result := 'Dos';
  39. else
  40. raise Exception.CreateFmt('TEnumHelper.ToString Value %d', [Ord(Value)]);
  41. end;
  42. end;
  43.  
  44. function TEnumHelper.ToString: string;
  45. begin
  46. Result := ToString(Self);
  47. end;

Ejemplo de uso:

delphi
  1. procedure TForm1.BitBtn1Click(Sender: TObject);
  2. var
  3. Enum: TEnum;
  4. begin
  5. Enum := TEnum.Parse(Edit1.Text);
  6. ShowMessage(Enum.ToString);
  7. end;


Editado por Agustin Ortu, 10 mayo 2016 - 09:25 .

  • 1

#7 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 775 mensajes
  • LocationArgentina

Escrito 10 mayo 2016 - 09:09

Otra cosa, no hay que usar la clase helper para invocar los metodos estaticos.
 
En Delphi es problematico porque en la ventana de autocompletado de codigo te sale asi:
 


delphi
  1. TEnumHelper.Parse
  2. TEnumHelper.ToString
  3. TEnumHelper. ...

Osea, sale como si fuera una "posible" sintaxis valida, pero resulta que despues no compila

 

Lo valido (y no que no sale en ese cuadro, para colmo :() es como puse en el ejemplo de arriba: TEnum.Parse

 

Osea el Helper extiende al tipo original, en un determinado scope 

 

Se puede interpretar como una suerte de "auto decorator" si se quiere

 

Cuando se trata de primitivos o enumerativos, te permite (como he dicho en otros post) cierta "syntaxis sugar", esa sintaxis fluida que parece que estas invocando metodos de un objeto pero es un simple enumerativo


Editado por Agustin Ortu, 10 mayo 2016 - 09:10 .

  • 0

#8 Delphius

Delphius

    Advanced Member

  • Administrador
  • 5.956 mensajes
  • LocationArgentina

Escrito 10 mayo 2016 - 09:20

Muchas gracias Agustín. Mañana lo pruebo.

Tengo el cerebro por demás quemado hace un buen rato y dejé de investigarlo. Demasiado código tuve el día de hoy... algo me dice que esta semana será larga.

 

Saludos,


  • 0

#9 Delphius

Delphius

    Advanced Member

  • Administrador
  • 5.956 mensajes
  • LocationArgentina

Escrito 11 mayo 2016 - 05:27

Interesante forma de encararlo Agustin.

He probado una versión alternativa, que prescinde de las funciones Parse() que tu tienes.

 

Básicamente se resume a algo como esto:


delphi
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$ModeSwitch typehelpers}
  5.  
  6. interface
  7.  
  8. uses
  9. Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
  10.  
  11. type
  12. TMiEnum = (meUno, meDos, meTres);
  13.  
  14. { TMiEnumHelper }
  15.  
  16. TMiEnumHelper = type helper for TMienum
  17. public
  18. class function ToChar(const AEnumValue: TMiEnum): Char; static;
  19. class function ToEnum(const ACharValue: char): TMiEnum; static;
  20. end;
  21.  
  22. { TForm1 }
  23.  
  24. TForm1 = class(TForm)
  25. Button1: TButton;
  26. Button2: TButton;
  27. procedure Button1Click(Sender: TObject);
  28. procedure Button2Click(Sender: TObject);
  29. private
  30. { private declarations }
  31. public
  32. { public declarations }
  33. end;
  34.  
  35. var
  36. Form1: TForm1;
  37.  
  38. implementation
  39.  
  40. {$R *.lfm}
  41.  
  42. { TForm1 }
  43.  
  44. procedure TForm1.Button1Click(Sender: TObject);
  45. var e: TMiEnum;
  46. begin
  47. e := meUno;
  48. ShowMessage(TMiEnum.ToChar(e));
  49. end;
  50.  
  51. procedure TForm1.Button2Click(Sender: TObject);
  52. var e: TMiEnum;
  53. begin
  54. e := TMiEnum.ToEnum('D');
  55. ShowMessage(e.ToChar(e));
  56. end;
  57.  
  58. { TMiEnumHelper }
  59.  
  60. class function TMiEnumHelper.ToChar(const AEnumValue: TMiEnum): Char;
  61. begin
  62. case AEnumValue of
  63. meUno: result := 'U';
  64. meDos: result := 'D';
  65. meTres: result := 'T';
  66. end;
  67. end;
  68.  
  69. class function TMiEnumHelper.ToEnum(const ACharValue: char): TMiEnum;
  70. begin
  71. case ACharValue of
  72. 'U': result := meUno;
  73. 'D': result := meDos;
  74. 'T': result := meTres;
  75. end;
  76. end;
  77.  
  78. end.

El ejemplo es muy sencillo. Pero ilustra que de una u otra forma se necesita, terminar pasando como parámetro el enumerativo para que se pueda efectuar la "conversión" al tipo char o string. Ya sea que se haga de forma implícita (o indirecta) como en tu caso, o bien explícitamente como en el mio.

No es posible hacer esto:


delphi
  1. class function ToChar: Char; static;
  2. begin
  3. case Self of
  4. meUno: result := 'U';
  5. ...
  6. end;
  7. end;

Necesariamente deberá pasarse como parámetro y es básicamente lo que termina haciendo tu implementación de ToString sobrecargada.

 

Para el botón 1, también se puede hacer esto:


delphi
  1. ShowMessage(e.ToChar(e));

Lo que ilustra un buen sin sentido.

 

La posible ventaja que tendríamos con este helper es que directamente podemos tener en el un buen abanico de implementaciones interesantes que hagan juego... casi como una clase más. Aunque para estar pasando parámetros y tener que de una u otra forma termine de hacer un TMiEnum.Algo() me hace cuestionar su sentido.

 

¿Que diferencia hay que tenga ahora un juego de funciones de conversión de uno y otro sentido "sueltas" o en una clase propia (por darle un nombre TEnumConverter) y tener el helper? Basicamente es lo mismo:

 

MiConverter.ToChar(mivarEnum);

 

que esto:

 

TMiEnum.ToChar(mivarEnum);

 

Parece que al menos, en los enumerativos, no supone demasiada ganancia esta nueva característica.

 

Saludos,


  • 1

#10 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 775 mensajes
  • LocationArgentina

Escrito 11 mayo 2016 - 05:45

Se puede hacer sin el parámetro, usando Self

Yo te mostré como lo uso yo porque me gusta tener las dos versiones, la estática y el método

no tengo pc a mano ahora para probarlo, en Delphi se puede

En el peor de los casos lo pones como private y listo
  • 0

#11 Delphius

Delphius

    Advanced Member

  • Administrador
  • 5.956 mensajes
  • LocationArgentina

Escrito 11 mayo 2016 - 06:52

Con el class function static no es posible hacer el case sobre el self de forma directa. Da error. Para solucionarlo debe pasarse como parámetro.

Es decir, si uno tiene pensado emplear class function, éstos requieren que sea estáticos, y a su vez provoca que no sepa interpretar el Self sobre el enumerativo. Debe recurrirse a ese "hack" que se ve en mi código:

 

case AEnumValue of ...

 

Para lograr que sea más "Self-pure-friendly" hay que prescindir del class y static. Lo siguiente compila sin problemas:


delphi
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$ModeSwitch typehelpers}
  5.  
  6. interface
  7.  
  8. uses
  9. Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
  10.  
  11. type
  12. TMiEnum = (meUno, meDos, meTres);
  13.  
  14. { TMiEnumHelper }
  15.  
  16. TMiEnumHelper = type helper for TMienum
  17. public
  18. function ToChar: Char;
  19. function ToEnum(AValue: char): TMiEnum;
  20. end;
  21.  
  22. { TForm1 }
  23.  
  24. TForm1 = class(TForm)
  25. Button1: TButton;
  26. Button2: TButton;
  27. procedure Button1Click(Sender: TObject);
  28. procedure Button2Click(Sender: TObject);
  29. private
  30. { private declarations }
  31. public
  32. { public declarations }
  33. end;
  34.  
  35. var
  36. Form1: TForm1;
  37.  
  38. implementation
  39.  
  40. {$R *.lfm}
  41.  
  42. { TMiEnumHelper }
  43.  
  44. function TMiEnumHelper.ToChar: Char;
  45. begin
  46. case self of
  47. meUno: result := 'U';
  48. meDos: result := 'D';
  49. meTres: result := 'T';
  50. end;
  51. end;
  52.  
  53. function TMiEnumHelper.ToEnum(AValue: char): TMiEnum;
  54. begin
  55. case AValue of
  56. 'U': result := meUno;
  57. 'D': result := meDos;
  58. 'T': result := meTres;
  59. end;
  60. end;
  61.  
  62. { TForm1 }
  63.  
  64. procedure TForm1.Button1Click(Sender: TObject);
  65. var e: TMiEnum;
  66. begin
  67. e := meUno;
  68. ShowMessage(e.ToChar);
  69. end;
  70.  
  71. procedure TForm1.Button2Click(Sender: TObject);
  72. var e: TMiEnum;
  73. begin
  74. e := TMiEnum.ToEnum('D');
  75. ShowMessage(e.ToChar);
  76. end;
  77.  
  78. { TMiEnumHelper }
  79.  
  80.  
  81.  
  82. end.

Que implicancias o desventajas cabría de esperarse en no usar los estáticos ni métodos de clase... no he analizado todavía. Pero al menos de esta forma en la única vía en que hay menos indirección.

 

Saludos,


  • 1

#12 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 775 mensajes
  • LocationArgentina

Escrito 11 mayo 2016 - 07:56

 

Con el class function static no es posible hacer el case sobre el self de forma directa. Da error. Para solucionarlo debe pasarse como parámetro.

Es decir, si uno tiene pensado emplear class function, éstos requieren que sea estáticos, y a su vez provoca que no sepa interpretar el Self sobre el enumerativo. Debe recurrirse a ese "hack" que se ve en mi código:

 

A eso mismo me referia (desde el telefono a veces me dan menos ganas de escribir :))

 

 

 

Que implicancias o desventajas cabría de esperarse en no usar los estáticos ni métodos de clase... no he analizado todavía. Pero al menos de esta forma en la única vía en que hay menos indirección.

 

No entiendo exactamente a que queres llegar (menos indireccion). Si te referis a eficiencia de ultima los podes declarar como inline. Los helper de Delphi, por ejemplo el de Integer, el metodo ToString lo define como inline e invoca a IntToStr(Self)

 

Yo siempre implemento las dos versiones, la estatica y la "normal" (como se le dice a este tipo de metodos?? laguna mental!!) por una cuestion de comodidad

 

La gran ventaja de hacerlo de esta manera y no como la vieja escuela es el scope (alcance) que queda mas limitado, y eso es siempre bueno

 

A la vieja escuela es tener funciones declaradas asi..

 



delphi
  1. function StrToEnum(const Value: string): TEnum;
  2. function StrToEstadoCivil(const Value: string): TEstadoCivil;
  3.  
  4. function EnumToStr(const Value: TEnum): string;
  5. function EstadoCivilToStr(const Value: TEstadoCivil): string; 

Estas tipeando codigo y para en la ventanita de sugerencias se te despliegan dos opciones

 

Las StrToXXX se "chocan" con todo! StrToInt, StrToFloat.. si le podes poner otro nombre, es cierto. 

 

De esta forma te queda:

 



delphi
  1. TEstadoCivil.[Parse/ToString/ToInteger/etc..]

Ponele que sea TextoToEnum, pero es lo mismo, te salen todos los TextoTo.. y tenes que elegir la adecuada. Parece tonto pero a mi me resulto muy util los type helper. Hoy los implemento para absolutamente todos los enumerativos

 

Tambien tengo metodos asi:



delphi
  1. TEstadoCivilHelper = type helper for TEstadoCivil
  2. public
  3. class function GetItems: TStrings; static; // devuelve un TStrings con todos los TEstadoCivil.ToStr
  4. class procedure AssignItems(AStrings: TSTrings); static; // Asigna GetItems al parametro AStrings
  5. end; 

Esos metodos los uso para asignar por ejemplo los estados civiles a un TComboBox

 

Ademas, siempre tengo la esperanza de que implementen herencia y/o genericos

 

Si los de FPC pudieron quiere decir que los de Embarcaderos son vagos! Osea yo quiero declarar un helper generico para los enumerativos y redefinir ToString! El resto es "siempre lo mismo"

 


  • 0

#13 Delphius

Delphius

    Advanced Member

  • Administrador
  • 5.956 mensajes
  • LocationArgentina

Escrito 11 mayo 2016 - 08:35

A eso mismo me referia (desde el telefono a veces me dan menos ganas de escribir :))

 

 

No entiendo exactamente a que queres llegar (menos indireccion). Si te referis a eficiencia de ultima los podes declarar como inline. Los helper de Delphi, por ejemplo el de Integer, el metodo ToString lo define como inline e invoca a IntToStr(Self)

 

Yo siempre implemento las dos versiones, la estatica y la "normal" (como se le dice a este tipo de metodos?? laguna mental!!) por una cuestion de comodidad

 

La gran ventaja de hacerlo de esta manera y no como la vieja escuela es el scope (alcance) que queda mas limitado, y eso es siempre bueno

 

A la vieja escuela es tener funciones declaradas asi..

 



delphi
  1. function StrToEnum(const Value: string): TEnum;
  2. function StrToEstadoCivil(const Value: string): TEstadoCivil;
  3.  
  4. function EnumToStr(const Value: TEnum): string;
  5. function EstadoCivilToStr(const Value: TEstadoCivil): string; 

Estas tipeando codigo y para en la ventanita de sugerencias se te despliegan dos opciones

 

Las StrToXXX se "chocan" con todo! StrToInt, StrToFloat.. si le podes poner otro nombre, es cierto. 

 

De esta forma te queda:

 



delphi
  1. TEstadoCivil.[Parse/ToString/ToInteger/etc..]

Ponele que sea TextoToEnum, pero es lo mismo, te salen todos los TextoTo.. y tenes que elegir la adecuada. Parece tonto pero a mi me resulto muy util los type helper. Hoy los implemento para absolutamente todos los enumerativos

 

Tambien tengo metodos asi:



delphi
  1. TEstadoCivilHelper = type helper for TEstadoCivil
  2. public
  3. class function GetItems: TStrings; static; // devuelve un TStrings con todos los TEstadoCivil.ToStr
  4. class procedure AssignItems(AStrings: TSTrings); static; // Asigna GetItems al parametro AStrings
  5. end; 

Esos metodos los uso para asignar por ejemplo los estados civiles a un TComboBox

 

Ademas, siempre tengo la esperanza de que implementen herencia y/o genericos

 

Si los de FPC pudieron quiere decir que los de Embarcaderos son vagos! Osea yo quiero declarar un helper generico para los enumerativos y redefinir ToString! El resto es "siempre lo mismo"

 

Por indirección me refiero a esto:

Enumerativo -> método clase estático -> método normal

Enumerativo -> método normal

 

Los caminos conducen a Roma. El 1er método cuenta con pase indirecto. El 2do es directo.
No me refería eficiencia, sino a ese detalle tan peculiar que, al menos en tu propuesta, me parece que podría evitarse. ¿Que se gana con que sea un método de clase y estático? La pregunta va en serio. Entiendo el objetivo de un método de clase cuando estamos hablando de una clase, pero ahora aplicado a un tipo de dato, como el numérico en este caso, ¿que rol cumple?

 

 

Yo tampoco recuerdo como es que se les llama a los "no dinámicos", no eres el único con lagunas mentales.

 

 

Por supuesto que es preferible algo que nos evite la vieja escuela. Y por eso es que me vi tentado en tratar de darle una oportunidad a los helpers.

 

Lo que me estuve preguntando después de estas prácticas es que tan diferente es tener por ejemplo una clase extra, que contar con el helper. Después de todo... ¡para ambos tenemos que declararlos e implementarlos! Si, te entiendo que la gracia del helper es que con el propio tipo podemos hacer nuevas cosas y se hace más amena la "lectura" del código. Pero  tampoco me parece que es tan pecaminoso MiClaseExtra.AlgoConEnum() contra un MiVarEnum.AlgoConEnum() o incluso el que debamos hacer el TMiEnum.AlgoParaObtenerEnum() como vamos a necesitar en ciertas ocaciones.
 

No me parece tonto que tu los uses. No te critico, en buena parte soy yo el rezagado. Vengo desde D6 en donde las cosas estaban lejos de esto y cuesta abandonar algunos vicios.

El ejemplo que das sobre GetItems para poner todo el listado en un combo me parece genial. ¡La verdad no me lo había pensado! ¡Y ahora que lo pones, le da un buen puntaje extra a los helpers!

 

Me llama la atención que digas que en FPC los helpers tengan herencia y no los de Delphi. Vi el artículo wiki de Lazarus que lo describe, y asumí que en Delphi las cosas serían igual. ¿No será que en las últimas versiones de Delphi se han puesto al corriente? No creo que les guste estar "detrás" de esa movida... cuando se está "promocionando" bastante los helpers ultimamente. O al menos a mi me da esa impresión (busquen sobre helpers y delphi y vean)

 

Saludos,


  • 0

#14 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 775 mensajes
  • LocationArgentina

Escrito 11 mayo 2016 - 08:57

No, los helper de Delphi no soportan herencia (1er punto critico)
 
El segundo punto aun más critico es que no se te permite extender una interface usando helpers
 
Lo ultimo que cambiaron en los helper hace poco en Delphi 10.1 Berlin, es que ahora no pueden acceder a la parte privada de las clases. Pueden acceder a la parte publica y protegida
 
En realidad la parte de las funciones estaticas es una
 
A mi la que mas me gusta y mas uso es la parte "normal", es decir el Enumerativo.ToString, Enumerativo.ToInteger, 
 
Pero lo de la herencia es una cagada! No solo para los helper que declara uno. Queres extender un helper que ya viene "de fabrica" y no podes! Osea quiero agregar un metodo a TStringHelper y cual es la solucion? Copiar y pegar todo el helper de nuevo y agregar los metodos nuevos!

  • 0

#15 Delphius

Delphius

    Advanced Member

  • Administrador
  • 5.956 mensajes
  • LocationArgentina

Escrito 16 mayo 2016 - 07:59

Ya me está convenciendo de sus utilidades el tema de helpers.

 

Hoy estuve divagando en una idea medio loca que podría ayudarme en mi proyecto. Tengo algunos tipos enumerativos definidos y pensé que ya que FPC ofrece la herencia para los helpers en tratar de hacer un helper base para mis enumerativos, con métodos como ToInteger, ToIndex, ToChar, ToString. Y que luego existan helpers que sobreescriban estos métodos para usarlos con el tipo enumerativo adecuado y ampliar las funcionalidades.

 

En principio la idea era buena... después de todo estos métodos tienen una "interfaz" común:


delphi
  1. function ToInteger: integer; abstract; virtual;
  2. function ToIndex: integer; abstract; virtual;
  3. function ToChar: char; abstract; virtual;
  4. function ToString: string; abstract; virtual;

Pero allí llegó mi cabeza y me dijo: Momentito... Para tener un helper base, éste aplicará a un tipo enumerativo en concreto. Ergo, cualquier helper que herede de éste también por fuerza deberá aplicar al mismo enumerativo.

 

Es decir, esto:


delphi
  1. TEnum1 = (...);
  2. TEnum2 = (...);
  3. //...
  4. TEnumN = (...);
  5.  
  6. THelper1 = type Helper(HelperBase) for TEnum1
  7. // etc...
  8. end;
  9. THelper2 = type Helper(HelperBase) for TEnum2
  10. // etc...
  11. end;
  12.  
  13. THelperN = type Helper(HelperBase) for TEnumN
  14. // etc...
  15. end;

No es posible.

 

Al momento de definir el HelperBase, este va "atado" a un enumerativo en cuestión:


php
  1. THelperBase = type helper for TEnum0 // Enum0 es otro enum cualquiera...
  2. // etc...

Y por tanto ahora, todo Enumerativo que lo extienda, debe ser para dicho Enum:


delphi
  1. THelper1 = type helper(HelperBase) for TEnum0
  2. // etc...
  3. end;

Avisenle a mis neuronas que no es posible crear un gato desde un perro. *-)  :D  Algo me dice que tengo que hacerme una nueva "mental note"  ;) 

 

Para llegar a una idea "similar" a esta, se me hace que si podría ser útil ya el enfoque de tener una clase TEnumClass (por darle un nombre) abstracta con métodos virtuales y abstractos. Luego extender de ésta para mis enumerativos. Esta clase ya me la imagino que la podría disponer en mis módulos bases. Este diseño incluso ya me justificaría una capa Services. Hasta el momento no veía necesario que disponga de una capa así, pero quizá sea sano que algunas cosas que ya tengo puedan ir a ésta. Lo estaba medio evitando para no estar añadiendo más unidades al proyecto.

 

Saludos,


  • 1

#16 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 775 mensajes
  • LocationArgentina

Escrito 17 mayo 2016 - 03:27

Estoy como el nene cuando le pinchan el globito..

 

Entonces los muchachos de FPC no "implementaron herencia en los helper"; seguro fue algo que "se les escapo" por como implementaron los helper; del mismo modo que Delphi se les "escapo" el acceder a metodos privados desde los helper

 

Yo crei que lo habian mejorado basandose tambien un poco en lo que protestamos los Delphinianos

 

En fin.. todo se resume en: 

 

Avisenle a mis neuronas que no es posible crear un gato desde un perro.

 


  • 0

#17 Delphius

Delphius

    Advanced Member

  • Administrador
  • 5.956 mensajes
  • LocationArgentina

Escrito 20 mayo 2016 - 09:11

Estoy como el nene cuando le pinchan el globito..

 

Entonces los muchachos de FPC no "implementaron herencia en los helper"; seguro fue algo que "se les escapo" por como implementaron los helper; del mismo modo que Delphi se les "escapo" el acceder a metodos privados desde los helper

 

Yo crei que lo habian mejorado basandose tambien un poco en lo que protestamos los Delphinianos

 

En fin.. todo se resume en: 

 

OJO. En parte tienen razón, si se está haciendo un helper para un tipo en cuestión, si se hereda dicho helper es natural que aplique al mismo tipo o especie. Tiene sentido esto cuando hablamos de que dicho tipo sea una clase y por tanto es de utilidad en un class helpers, o bien en un record helper (aunque no he probado con esto último) ya que desde una clase o un record podemos ir ampliando ramas.

El asunto es que cuando el tipo al que vamos a "hackear" es un enumerativo las cosas son un tanto diferentes. Sino pensemos: ¿Podemos definir un sub tipo enumerativo? Que yo sepa no:


delphi
  1. type
  2. TEnumBase = (ebUno, ebDos, ebTres, ...);
  3.  
  4. TSubEnum = (// ¿Cómo indico que este Sub enumativo sea una "especie" de TEnumBase?);

Cada enumativo es una "raiz". Es independiente de otro ¡No admite ramas! Por tanto intentar hacer sub enumerativos, e incluso pretender que un enumerative helper definido para un enumativo en particular aplique a otro carece de sentido.

Lo que si es legal es heredar un enumerative helper para el mismo enumativo. En ocasiones puede ser por razones de legibilidad y necesidad ir ampliando con nuevas características y hasta incluso sobreescribir otras.

 

Lo que podríamos probar es si tiene sentido una herencia sobre "subtipo" en los conjuntos (Al menos matemáticamente el concepto de subconjunto existe) No hice experimentos para comprobar si es posible hacer un set helper, y por consiguiente heredar de éste. Es decir intentar definir un set, un helper para éste y luego hacer un subset y un sub helper para el subset.

 

A pesar de que el concepto de los helpers es una vía para "cruzar" la barrera del POO y "meternos en donde no se debe por la puerta trasera" debe seguir los mismos principios elementales: herencia y polimorfismo.

 

Saludos,


  • 0

#18 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 775 mensajes
  • LocationArgentina

Escrito 04 febrero 2017 - 01:56

Quiza esto pueda ser viable


  • 0





Etiquetado también con una o más de estas palabras: enumerativo, record helper, string