TernaryOperator
A unit TernaryOperator implementa um operador ternário para Delphi, utilizando Custom Managed Records (Delphi 10.4+).
- Active · last change 5-25-2026
- ★ 3 on GitHub
README
TernaryOperator
A unit TernaryOperator implementa um operador ternário para Delphi, utilizando Custom Managed Records (Delphi 10.4+).
Propósito
Fornecer uma sintaxe concisa e type-safe para expressões condicionais, permitindo:
- Substituir
if-then-elsesimples por expressões ternárias - Encadear múltiplas condições (if-else if-else)
- Executar procedures como efeitos colaterais
- Manter type-safety com genéricos
Requisitos
- Delphi 10.4+ (suporte a Custom Managed Records)
- Compilação em modo de 64 bits recomendado
Componentes
TAction - Delegate
TAction = reference to procedure;Define um delegate para procedures sem retorno. Usado para encapsular efeitos colaterais opcionais.
TTernary<T> - Record Genérico
Custom Managed Record que implementa o operador ternário.
Campos Privados
FCondition: Boolean- Armazena a condição avaliadaFTrue: T- Armazena o valor para condição verdadeiraFAction: TAction- Armazena a procedure opcional
Métodos
class operator Initialize
class operator Initialize (out Dest: TTernary<T>);Chamado automaticamente quando o record é criado. Inicializa os campos com valores padrão.
Efeito: Garante estado consistente ao criar um novo TTernary<T>.
class operator Finalize
class operator Finalize (var Dest: TTernary<T>);Chamado automaticamente quando o record sai de escopo. Responsável pela limpeza automática de tipos managed (strings, interfaces, arrays dinâmicos).
Efeito: Garante limpeza mesmo em caso de exceção (try-finally automático).
class function New
class function New(ACondition: Boolean; const ATrue: T; AAction: TAction = nil): TTernary<T>; static; inline;Factory method que cria uma nova expressão ternária.
Parâmetros:
ACondition: Boolean- A condição a ser avaliadaATrue: T- Valor retornado se condição é verdadeiraAAction: TAction = nil- Procedure opcional a executar de acordo com a condição pretendida para uma ação que tando pode ser em True ou False, como demonstrado no exemplo.
Retorno: TTernary<T> com a condição e valores configurados
Exemplo:
var resultado := TTernary<string>.New(idade > 18, 'Maior');function Pipe
function Pipe(const AFalse: T; AAction: TAction = nil): T; inline;Executa a avaliação do ternário e retorna o valor apropriado.
Parâmetros:
AFalse: T- Valor retornado se condição é falsaAAction: TAction = nil- Procedure opcional a executar se condição é falsa
Retorno: Valor de FTrue (de New) se condição é verdadeira, ou AFalse caso contrário
Comportamento:
- Se
FConditionétrue: executaFAction(se definida) e retornaFTrue - Se
FConditionéfalse: executaAAction(se definida) e retornaAFalse
Exemplo:
var status := TTernary<string>.New(ativo, 'Ativo').Pipe('Inativo');Exemplos de Uso
Exemplo 1: Básico - Sem Procedures
var
idade: Integer := 25;
categoriaIdade: string;
begin
categoriaIdade := TTernary<string>.New(idade >= 18, 'Maior').Pipe('Menor');
ShowMessage(categoriaIdade); // Mostra: "Maior"
end;Exemplo 2: Com Procedures - Efeitos Colaterais (demo)
procedure TFormPrincipal.CheckBoxHabilitadoClick(Sender: TObject);
begin
// Exemplo utilizando TAction
LabelStatus.Caption := TTernary<String>.New(CheckBoxHabilitado.Checked,
'Habilitado',
procedure
begin
LabelStatus.Font.Color := clGreen;
end).Pipe('Desabilitado',
procedure
begin
LabelStatus.Font.Color := clRed;
end);
var
resultado := TTernary<String>.New(CheckBoxHabilitado.Checked, 'Habilitado',
procedure
begin
// faça qualquer coisa
end).Pipe('Desabilitado',
procedure
begin
// faça qualquer coisa
end);
CheckBoxHabilitado.Caption := resultado;
// Exemplo de uso simples (sem TAction)
// LabelStatus.Caption := TTernary<String>.New(CheckBoxHabilitado.Checked,
// 'Habilitado').Pipe('Desabilitado');
//
// CheckBoxHabilitado.Caption := TTernary<String>.New(CheckBoxHabilitado.Checked,
// 'Habilitado').Pipe('Desabilitado');
end;Exemplo 3: Encadeamento - Múltiplas Condições
var
valor: Integer := 50;
classificacao: string;
begin
classificacao := TTernary<string>.New(valor > 100, 'Grande').Pipe(
TTernary<string>.New(valor = 100, 'Exato').Pipe('Pequeno')
);
ShowMessage(classificacao); // Mostra: "Pequeno"
end;Exemplo 4: Encadeamento com Procedures
procedure TFormPrincipal.AvaliarValor;
var
valor: Integer := 75;
resultado: string;
begin
resultado := TTernary<string>.New(valor > 100,
Format('Valor %d é grande', [valor]),
procedure
begin
ProgressBar1.Position := 100;
end).Pipe(TTernary<string>.New(valor = 100,
Format('Valor %d é exato', [valor]),
procedure
begin
ProgressBar1.Position := 100;
end).Pipe(Format('Valor %d é pequeno', [valor]),
procedure
begin
ProgressBar1.Position := valor;
end));
LabelResultado.Caption := resultado;
end;Exemplo 5: Com Tipos Diferentes
var
idade: Integer := 25;
categoriaIdade: string;
desconto: Double;
begin
// String
categoriaIdade := TTernary<string>.New(idade >= 18, 'Adulto').Pipe('Menor');
// Integer
var nivelAcesso := TTernary<Integer>.New(idade >= 21, 5).Pipe(3);
// Double
desconto := TTernary<Double>.New(idade >= 60, 0.20).Pipe(0.10);
end;Padrões de Uso
Padrão 1: Valor Simples (Recomendado para Casos Simples)
var status := TTernary<string>.New(ativo, 'Ativo').Pipe('Inativo');
Label1.Caption := status;Padrão 2: Apenas Efeitos Colaterais (Sem Retorno Usado)
TTernary<string>.New(ativo, 'dummy',
procedure
begin
// Faça algo
end).Pipe('dummy',
procedure
begin
// Faça algo diferente
end);Padrão 3: Valor + Efeitos Colaterais
var resultado := TTernary<string>.New(condicao, 'ValorA',
procedure
begin
// Efeito A
end).Pipe('ValorB',
procedure
begin
// Efeito B
end);
Label1.Caption := resultado;Padrão 4: Múltiplas Condições (if-else if-else)
var resultado := TTernary<string>.New(condicao1, 'Valor1').Pipe(
TTernary<string>.New(condicao2, 'Valor2').Pipe(
TTernary<string>.New(condicao3, 'Valor3').Pipe('ValorPadrao')
)
);Detalhes de Implementação
Custom Managed Records
A unit utiliza Custom Managed Records do Delphi 10.4+:
- Initialize: Garante inicialização automática
- Finalize: Limpeza automática com suporte a exceções
- Sem alocação em heap (stack-based)
- Suporte completo a tipos managed (strings, interfaces)
Type Safety
Utiliza genéricos <T> para garantir type-safety em tempo de compilação:
// Correto
var s := TTernary<string>.New(true, 'Texto').Pipe('Outro');
// Erro: Type mismatch
var s := TTernary<string>.New(true, 'Texto').Pipe(123);Performance
- Métodos
inlinepara zero overhead - Stack allocation (sem GC)
- Sem boxing/unboxing para tipos value
Limitações e Considerações
Sintaxe Limitada
Delphi não permite sobrecarregar os operadores ? e :, então:
- C#:
condicao ? Valor-para-True : Valor-para-False - Delphi:
TTernary<T>.New(condicao, valor1).Pipe(valor2)
Procedures Sempre Executam
Ao encadear TTernary, todas as procedures no caminho são executadas:
// Ambas as procedures são executadas se condicao = false
TTernary<string>.New(condicao, 'A', procedure begin WriteLn('A'); end)
.Pipe(TTernary<string>.New(condicao2, 'B', procedure begin WriteLn('B'); end)
.Pipe('C', procedure begin WriteLn('C'); end));Solução: Coloque ShowMessage ou operações críticas fora das procedures:
var resultado := TTernary<string>.New(condicao, 'A',
procedure begin
Label1.Caption := 'A'; // OK: apenas UI
end).Pipe('B');
ShowMessage(resultado); // ShowMessage aqui, não dentro da procedureComparação com Alternativas
| Abordagem | Sintaxe | Legibilidade | Segurança | Performance |
|---|---|---|---|---|
if-then-else |
Longa | Boa | Excelente | Excelente |
IfThen RTL |
IfThen(cond, A, B) |
Boa | Boa | Excelente |
TTernary |
TTernary<T>.New(cond, A).Pipe(B) |
Excelente | Excelente | Excelente |
Boas Práticas
- Use para decisões simples: 2-3 opções no máximo
- Encadeie com cautela: Mais de 3 níveis fica complexo
- Evite lógica pesada: Use procedures para efeitos colaterais simples
- Sempre use tipos genéricos: Deixe o compilador verificar tipos
- Teste procedures separadamente: Elas executam mesmo em encadeamentos
Troubleshooting
Problema: "Type parameters not allowed on global procedure"
Solução: Usando Delphi 10.3 ou anterior? Atualize para 10.4+.
Problema: Procedures executam múltiplas vezes
Solução: É o comportamento esperado em encadeamentos. No caso do exemplo, Movi ShowMessage para fora.
Problema: Tipo não encontrado
Solução: Adicione uses TernaryOperator; no arquivo.
Arquivos Relacionados
TernaryOperator.pas- Unit principalUprincipal.pas- Exemplos básicos, com sobreposição e procedures
Versão
- Versão: 1.0
- Compatibilidade: Delphi 10.4+
- Autor: A. S. Santos (suporte-dev)
Licença
MIT.

