我猜我把手远离德尔福太久了,我想;在过去的几年里,我一直忙于Java和PHP。现在,当我回到做一点Delphi工作时,我意识到我真的很想念Java和PHP都支持的条件运算符。
在Delphi程序中你会找到多少这样的行?
var s : string;
begin
...<here the string result is manipulated>...
if combo.Text='' then
s := 'null'
else
s := QuotedStr(combo.Text);
result := result + s;
end;
其中一个简单的
result := result + (combo.text='')?'null':quotedStr(combo.text);
就足够了。我喜欢这个,它不仅缩短了代码,这样我也避免声明一些帮助s:string
变量。
为什么条件运算符不属于Delphi并且 - 它们是否会得到支持?我注意到2009版Delphi(泛型)有很多语言扩展,为什么不添加这个功能呢?
答案 0 :(得分:41)
这样的运营商不是当前Delphi版本的一部分,因为它不是以前版本的一部分,并且需求不足以证明添加它的成本是合理的。 (您会发现该解释适用于您希望在批次产品中使用的 lot 功能。)
Delphi在Math和StrUtils单元中提供了一组IfThen
函数,但它们具有评估它们的值参数的不幸属性,因此这样的代码将失败:
Foo := IfThen(Obj = nil, '<none>', Obj.Name);
要真正做到正确,需要得到编译器的帮助。在Delphi社区中,我感觉到使用问号和冒号一般不喜欢C风格的语法。我见过会使用这样语法的提案:
Foo := if Obj = nil then
'<none>'
else
Obj.Name;
使条件运算符如此具有吸引力的部分原因在于它们可以让您编写简洁的代码,但是Delphi的编写风格使得上述内容无法实现,即使将所有内容放在一行上也是如此。
它实际上不需要采用运算符的形式。 Delphi Prism提供了一个编译器魔术函数Iif
,它仅评估其两个值参数之一:
Foo := Iif(Obj = nil, '<none>', Obj.Name);
你问为什么这样的功能不会与Delphi 2009中添加的所有其他语言功能一起添加。我认为这是你的理由。还有许多其他的语言变化已经需要精细处理;开发人员不需要承担更多的负担。功能不是免费的。
你问德尔福是否会有这样的功能。我不知道Embarcadero的计划会议,我不得不把我的水晶球送去修理,所以我不能肯定地说,但我预测如果它会有这样的功能,它将以Delphi Prism的Iif
函数的形式出现。这个想法出现在the discussion in Quality Central的末尾附近,并且提出异议,作为一个新的保留字,它会破坏与已定义具有相同名称的函数的其他人的代码的向后兼容性。但是,这不是一个有效的对象,因为它不需要是一个保留字。它可以是一个标识符,就像Writeln
和Exit
一样,即使系统单元中的一个被特殊处理,它也可以在其他单元中重新定义。
答案 1 :(得分:5)
关于此问题的{QC报告(8451)有一个合理的讨论。
2004年6月提出,Borland / CodeGear / Embarcadero似乎没有任何回应。
答案 2 :(得分:5)
重载的IFTHEN函数有许多可用的简单类型句柄。
StrUtils.IfThen
(String
)
Math.IfThen
(Integer
)
Math.IfThen
(Int64
)
Math.IfThen
(Double
)(也适用于TDateTime
)
这个模型如Andreas所评论的示例所示,但对于简单类型,这是非常合理的。如果遵循Delphi / Pascal方法的约定而不是屈服于使用尽可能少的字符的C方式。
就个人而言,我宁愿看不到Delphi中引入的条件运算符(即?:
),因为我更喜欢Delphi / Pascal对C及其派生语言的可读性。我希望看到更多创新的Delphi类型解决方案,而不是实现更多的C-isms。
答案 3 :(得分:5)
确定。当天的WTF代码:)
如何获得主要像三元/条件函数的东西。
program Project107;
{$APPTYPE CONSOLE}
uses SysUtils;
type
TLazyIfThen<T:record>=record
class function IfThen(aCondition:Boolean;aIfTrue, aIfFalse:TFunc<T>):T; static;
end;
class function TLazyIfThen<T>.IfThen(aCondition:Boolean;aIfTrue, aIfFalse:TFunc<T>):T;
begin
if aCondition then
Result := aIfTrue
else
Result := aIfFalse
end;
begin
WriteLn(
TLazyIfThen<Integer>.IfThen(
True,
function:Integer begin result := 0 end,
function:Integer begin result := 1 end
)
);
ReadLn;
end.
是的,它或多或少都没用,但它表明它可以完成。
答案 4 :(得分:4)
Delphi中没有条件运算符,我很怀疑是否会有一个,但你可能永远都不知道。您始终可以在Embarcadero发出请求。
另一种方法是定义Iff函数:
function Iff(const ACondition: Boolean; const ATrueValue, AFalseValue: XXX): XXX;
begin
if ACondition then
Result := ATrueValue
else
Result := AFalseValue;
end;
XXX是欲望类型。
用作:
Result := Result + Iff(combo.text='', 'null', quotedStr(combo.text));
为什么不实现条件运算符有几个原因。其中之一是可读性。 Pascal(以及Delphi)更多地集中在可读性而不是C语言语言,这些语言更多地集中在字符功能上(每个字符尽可能多的信息)。条件运算符很强大,但(根据一些)不可读。但是如果你看一下Delphi中的(可怕的)声明......(不需要多说)。 另一个原因是不需要条件运算符。这是真的。但还有更多要求尚未实施。
最后,这只是一个品味问题。
但是如果你只想要评估一个参数,你总是可以使用下面的内容,这违反了字符功能概念的可读性:
[overdesignmode]
// Please don't take this that serious.
type
TFunc = function(): XXX;
function Iff(const ACondition: Boolean; const ATrueFunc, AFalseFunc: TFunc): XXX;
begin
if ACondition then
ATrueFunc
else
AFalseFunc;
end;
[/ overdesignmode]
答案 5 :(得分:3)
答案 6 :(得分:1)
实际上对于字符串,您可以使用: StrUtils.IfThen 函数:
function IfThen(AValue: Boolean;
const ATrue: string;
AFalse: string = ): string; overload;
查看delphi帮助wiki:http://docwiki.embarcadero.com/VCL/en/StrUtils.IfThen
它完全符合您的需要。
答案 7 :(得分:1)
另一种选择是使用泛型:
Cond<T> = class
public class function IIF(Cond: boolean; IfVal: T; ElseVal: T): T;
end;
implementation
class function Cond<T>.IIF(Cond: boolean; IfVal, ElseVal: T): T;
begin
if Cond then
Result := IfVal
else
Result := ElseVal;
end;
这非常易读:
var MyInt: Integer;
begin
MyInt:= Cond<Integer>.IIF(someCondition, 0, 42);
注意:正如Alan Bryant(2004年6月21日上午7:26:21)在QR 8451中指出的那样,这将始终评估所有3个参数 - 所以要注意它不是真正的三元运算符
答案 8 :(得分:0)
更好的是一个支持多种数据类型和结果的重载IIF(内联if)。
答案 9 :(得分:0)
Jedi Code Library(JCL)使用一组名为Iff()的函数实现了三元操作。请参阅此处获取文档:
http://wiki.delphi-jedi.org/wiki/JCL_Help:Iff@Boolean@Boolean@Boolean
要下载JCL,您可以访问此站点:
答案 10 :(得分:0)
当天的WTF代码Nr。 2:
program TernaryOpTest;
uses
System.SysUtils, Vcl.Dialogs;
type
TGetValue = reference to function(): Double;
function TernaryOp(condition: Boolean; trueFunc, falseFunc: TGetValue): Double;
begin
if condition then begin
if Assigned(trueFunc) then Result := trueFunc() else raise EArgumentNilException.Create('trueFunc not defined.');
end
else begin
if Assigned(falseFunc) then Result := falseFunc() else raise EArgumentNilException.Create('falseFunc not defined.');
end;
end;
procedure TernaryTest(x: Double);
var
v: Double;
begin
v := TernaryOp(x <> 0, function(): Double begin Result := 1/x; ShowMessage('True case'); end, function(): Double begin Result := 0; ShowMessage('False case'); end);
ShowMessage(FloatToStr(v));
end;
begin
ShowMessage('Testing true case');
TernaryTest(10);
ShowMessage('Testing false case');
TernaryTest(0);
ShowMessage('Testing exception');
TernaryOp(False, nil, nil);
end.
对Variant数据类型的一种可能的修改是:
type
TGetValue = reference to function(): Variant;
function TernaryOp(condition: Boolean; trueFunc, falseFunc: TGetValue): Variant;
begin
Result := Unassigned;
if condition then begin
if Assigned(trueFunc) then Result := trueFunc();
end
else begin
if Assigned(falseFunc) then Result := falseFunc();
end;
end;