【问题标题】:Why am I getting list index out of bounds (1) error?为什么我的列表索引越界 (1) 错误?
【发布时间】:2015-03-11 18:58:05
【问题描述】:

我收到此错误消息:

列表索引越界 (1)

尝试从我的数据库中选择信息时。我正在使用 Delphi XE7、MySQL 6.2、FDConnection 和 FDQuery。我的代码是:

Procedure TAppointmentForm.GetTreatPrice;
Begin
  TreatPriceQuery.SQL.Text:= 'Select Cost from Treatment where TreatName = '+quotedstr(Treatment)+'';
  TreatPriceQuery.Open;
  TreatPrice:= TreatPriceQuery.FieldByName('Cost').AsInteger;
End;

我正在使用 CheckBoxList 来获取 Treatment。我的代码是:

Procedure TAppointmentForm.GetAppCost;
Var
  Count: Integer;
begin
  for Count := 0 to (Count1-1) do
    Begin
      if TreatmentCheckListBox.State[Count] = cbChecked then
        Begin
          Treatment:= TreatmentCheckListBox.Items.Strings[Count];
          GetTreatPrice;
          AppCost:= AppCost + TreatPrice;
        End
      Else
        AppCost:= AppCost;
    End;
end;

【问题讨论】:

  • GetAppCost() 的循环中,Count1 来自哪里,分配给它的是什么?循环需要使用来自TreatmentCheckListBox.Items.Count 的值,如果它还没有这样做的话。
  • 另外,为什么要使用全局/成员变量来传递 TreamentTreatPrice 值?为什么不直接给GetTreatPrice()一个输入参数和一个返回值呢?
  • Count1 是我用来将它们添加到列表中的变量
  • 那么Count1 可能与实际的Items.Count 不同步。您应该在循环中使用实际的Items.CountAppCost:= AppCost 也是多余的。

标签: mysql delphi delphi-xe7 firedac


【解决方案1】:

您的代码过于复杂。您可以使用TCheckListBoxChecked 属性,在访问内容中的项目时完全删除StringsStringsItems 的默认属性)。此外,您应该在循环中使用 CountItems

Procedure TAppointmentForm.GetAppCost;
Var
  Idx: Integer;
begin
  for Idx := 0 to TreatmentCheckListBox.Items.Count - 1 do
  Begin
    if TreatmentCheckListBox.Checked[Idx] then
    Begin
      Treatment:= TreatmentCheckListBox.Items[Idx];
      GetTreatPrice;
      AppCost:= AppCost + TreatPrice;
    End;
  // The next two lines are a non operation. Assigning a
  // variable to itself does nothing. Remove them entirely
 // Else
 //  AppCost:= AppCost;
    End;
end;

此外,停止为您的 SQL 连接文本,并改用参数化查询,以提高效率和防止 SQL 注入。

Procedure TAppointmentForm.GetTreatPrice;
Begin
  TreatPriceQuery.SQL.Text:= 'Select Cost from Treatment where TreatName = :TreatName';
  TreatPriceQuery.ParamByName('TreatName').AsString := Treatment;
  TreatPriceQuery.Open;
  TreatPrice:= TreatPriceQuery.FieldByName('Cost').AsInteger;
End;

我也同意@Remy 在对您问题的评论中的内容。您应该传递参数而不是使用全局变量。

function TAppointmentForm.GetTreatPrice(const TreatmentName: String): Integer;
Begin
  TreatPriceQuery.SQL.Text:= 'Select Cost from Treatment where TreatName = :TreatName';
  TreatPriceQuery.ParamByName('TreatName').AsString := TreatmentName;
  TreatPriceQuery.Open;
  Result := TreatPriceQuery.FieldByName('Cost').AsInteger;
End;

Procedure TAppointmentForm.GetAppCost;
Var
  Idx: Integer;
begin
  AppCost := 0;
  for Idx := 0 to TreatmentCheckListBox.Items.Count - 1 do
  Begin
    if TreatmentCheckListBox.Checked[Idx] then
    Begin
      AppCost := AppCost + GetTreatPrice(TreatmentCheckListBox.Items[Idx]);
    End;
  End;
end;

【讨论】:

  • 并且不要忘记在运行循环之前初始化AppCost
  • 由于使用了 QuotedStr 函数,OP 列出的代码不易受到 SQL 注入的影响。
  • @HeartWare:SQL 连接容易受到 SQL 注入一般的影响。虽然它可能不在此特定用途中,但它是一种可怕的做法,应该从一开始就进行教育。我不知道你为什么反对开始这种教育。但感谢您的反馈。
  • 我并没有说使用 SQL 参数是一个坏主意,但我多年来一直使用 QuotedStr 原理,没有任何问题,如果你习惯这样做,它和参数一样安全。我的评论只是为了说明参数有个替代方案,它们同样安全。人们假设我在 SO 上编写的一些代码容易受到 SQL 注入(使用 QuotedStr)的影响,但每次我要求他们证明这一点时,他们都无法做到。对于某些人来说,“使用参数”已经成为一种口头禅,没有必要在所有情况下都使用它。
  • @HeartWare:不使用参数还会导致格式化日期、转换数字、转换逻辑(布尔)字段和其他问题的困难。 IMO 当参数更容易和更安全地执行任何操作时,对任何事情使用 SQL 连接都是错误的。另外,多次运行的语句在使用参数时可以被DB编译和缓存,这是动态SQL无法做到的。使用参数还有更多的优势。但这正在脱轨 - 这个问题(和我的答案)是关于“列表索引超出范围”的。
猜你喜欢
  • 1970-01-01
  • 2018-06-27
  • 2013-06-06
  • 1970-01-01
  • 2021-11-09
  • 2014-06-06
  • 2015-03-22
相关资源
最近更新 更多