delphi - suma - Inicializar el resultado de la función de cadena?
resta de cadenas (7)
Esto no es un error, sino una " feature ":
Para una cadena, matriz dinámica, puntero de método o resultado de variante, los efectos son los mismos que si el resultado de la función se declarase como un parámetro var adicional siguiendo los parámetros declarados. En otras palabras, la persona que llama pasa un puntero adicional de 32 bits que apunta a una variable en la que devolver el resultado de la función.
Es decir, tu
function TMyObject.GenerateInfo: string;
Es realmente esto:
procedure TMyObject.GenerateInfo(var Result: string);
Tenga en cuenta el prefijo " var " (¡no " fuera " como puede esperar!).
Esto es TAN poco intuitivo, por lo que conduce a todo tipo de problemas en el código. Código en cuestión: solo un ejemplo de los resultados de esta función.
Ver y votar por esta solicitud .
He estado depurando un problema con una función que devuelve una cadena que me tiene preocupado. Siempre he supuesto que la variable de resultado implícita para las funciones que devuelven una cadena estaría vacía al comienzo de la llamada a la función, pero el siguiente código (simplificado) produjo un resultado inesperado:
function TMyObject.GenerateInfo: string;
procedure AppendInfo(const AppendStr: string);
begin
if(Result > '''') then
Result := Result + #13;
Result := Result + AppendStr;
end;
begin
if(ACondition) then
AppendInfo(''Some Text'');
end;
Llamar a esta función varias veces dio como resultado:
"Some Text"
la primera vez,
"Some Text"
"Some Text"
la segunda vez,
"Some Text"
"Some Text"
"Some Text"
la tercera vez, etc.
Para solucionarlo tuve que inicializar el resultado:
begin
Result := '''';
if(ACondition) then
AppendInfo(''Some Text'');
end;
¿Es necesario inicializar un resultado de función de cadena? ¿Por qué (técnicamente)? ¿Por qué el compilador no emite una advertencia "W1035 El valor de retorno de la función ''xxx'' podría estar indefinido" para las funciones de cadena? ¿Debo revisar todo mi código para asegurarme de que se establece un valor, ya que no es confiable esperar una cadena vacía de una función si el resultado no está establecido explícitamente?
Lo he probado en una nueva aplicación de prueba y el resultado es el mismo.
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
S: string;
begin
for i := 1 to 5 do
S := GenerateInfo;
ShowMessage(S); // 5 lines!
end;
Esto no es un error. Por definición, no se inicializa ninguna variable dentro de la función, incluido el resultado.
Por lo tanto, su resultado no se encuentra en la primera llamada y puede contener cualquier cosa. Cómo se implementa en el compilador es irrelevante, y puede tener diferentes resultados en diferentes compiladores.
Esto parece un error en D2007. Acabo de probarlo en Delphi 2010 y obtuve el comportamiento esperado. (1 línea en lugar de 5.)
La respuesta de Alex casi siempre es correcta y responde por qué estaba viendo el comportamiento extraño que era, pero no es toda la historia.
Lo siguiente, compilado sin optimización, produce el resultado esperado de que sTemp sea una cadena vacía. Si cambia la función para la llamada de procedimiento, obtendrá un resultado diferente.
Parece que hay una regla diferente para la unidad de programa real.
Es cierto que este es un caso de esquina.
program Project1;
{$APPTYPE CONSOLE}
uses System.SysUtils;
function PointlessFunction: string;
begin
end;
procedure PointlessProcedure(var AString: string);
begin
end;
var
sTemp: string;
begin
sTemp := ''1234'';
sTemp := PointlessFunction;
//PointlessProcedure(sTemp);
WriteLn(''Result:'' + sTemp);
ReadLn;
end.
Nos hemos topado con esto antes, creo que ya desde Delphi 6 o 7. Sí, aunque el compilador no se molesta en advertirte, necesitas inicializar las variables de resultado de la cadena, precisamente por el motivo te encontraste La variable de cadena se está inicializando, no se inicia como una referencia de basura, pero parece que no se inicializa cuando se espera.
En cuanto a por qué sucede ... no estoy seguro. Es un error, por lo que no necesariamente necesita una razón. Solo lo vimos suceder cuando llamamos repetidamente a la función en un bucle; si lo llamamos fuera de un bucle, funcionó como se esperaba. Parecía que la persona que llamaba estaba asignando espacio para la variable Resultado (y reutilizándola cuando llamó a la misma función repetidamente, causando el error), en lugar de asignar su propia cadena (y asignar una nueva en cada llamada).
Si usaba cadenas cortas, entonces la persona que llama asigna el búfer; ese es un comportamiento de larga data para tipos de valores grandes. Pero eso no tiene sentido para AnsiString. Quizás el equipo compilador simplemente olvidó cambiar la semántica cuando implementaron por primera vez cadenas largas en Delphi 2.
Parece que tu función debería simplificarse así:
function TMyObject.GenerateInfo: string;
begin
if(ACondition) then
Result := ''Some Text''
else
Result := '''';
end;
Normalmente no desea utilizar el resultado en el lado derecho de una tarea en una función.
De todos modos, estrictamente para fines ilustrativos, también podría hacer esto, aunque no se recomienda:
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
S: string;
begin
for i := 1 to 5 do
begin
S := ''''; // Clear before you call
S := GenerateInfo;
end;
ShowMessage(S); // 5 lines!
end;
Si crees que se hace una gestión automática de las cadenas para hacerte la vida más fácil, en parte tienes razón. Todas estas cosas también se hacen para que la lógica de la cadena sea coherente y los efectos secundarios libres.
En muchos lugares hay cadenas pasadas por referencia, pasadas por valor, pero todas estas líneas esperan CADENAS VÁLIDAS en cuyo contador de administración de memoria es válido, no es un valor basura. Por lo tanto, para mantener las cadenas de caracteres válidas, lo único seguro es que deberían inicializarse cuando se introdujeron por primera vez. Por ejemplo, para cualquier cadena variable local, esta es una necesidad ya que este es el lugar donde se introduce una cadena. El resto del uso de cadenas, incluido function (): string (que en realidad es el procedimiento (var Result: string) que Alexander señaló correctamente) solo espera cadenas válidas en la pila, no inicializadas . Y la validez aquí viene del hecho de que la construcción (var Result: string) dice que "estoy esperando una variable válida que definitivamente se introdujo antes". ACTUALIZACIÓN: Debido a que el contenido real de Resultado es inesperado, pero debido a la misma lógica, si es la única llamada a esta función que tiene una variable local a la izquierda, el vacío de la cadena en este caso está garantizado.