Objects
Um objeto no AutoHotkey é um tipo de dados abstrato que
fornece três funções básicas:
- GET a value.
- SET a value.
CALL um método (isto é,
uma função que faz algo com o objeto de destino).
Tópicos relacionados:
- Objects: explicação geral dos objetos.
IsObject pode ser usado para determinar se um valor é um objeto:
Tipos de objetos incluem:
ComObject - envolve uma interface IDispatch (um objeto COM ou
"Automation").
O suporte para objetos requer
[v1.0.90+], mas alguns recursos podem exigir uma versão posterior.
Table of Contents
- Uso
Básico – Matrizes
Simples, Matrizes
Associativas, Objetos,
Objetos
de liberação, Observações
- Uso
Extendido – Referências
de Função, Matrizes
de Matrizes, Matrizes
de funções
- Objetos
Personalizados - Protótipos,
Classes,
Construção
e Destruição, Meta-Funções
- Objeto
Base Padrão – Inicialização
Var Automática, Pseudo-Propriedades,
Depuração
- Implementação – Contagem
de Referência, Ponteiros
para Objetos
Uso Básico
Crie uma matriz:
Array :=
[Item1, Item2, ..., ItemN]
Array := Array(Item1,
Item2, ..., ItemN)
Recuperar um item:
Value :=
Array[Index]
Atribuir um item:
Array[Index]
:= Value
Insira um ou mais itens em um determinado índice:
Array.InsertAt(Index,
Value, Value2, ...)
Anexe um ou mais itens:
Array.Push(Value,
Value2, ...)
Remover um item:
RemovedValue
:= Array.RemoveAt(Index)
Remova o último item:
RemovedValue
:= Array.Pop()
Se a matriz não estiver vazia, MinIndex and MaxIndex/Length
retornarão
o índice mais baixo e mais alto atualmente em uso na matriz. Como o índice mais
baixo é quase sempre 1, o MaxIndex geralmente retorna o número de itens. No
entanto, se não houver chaves inteiras, o MaxIndex retorna uma string vazia,
enquanto Length retorna 0. O loop através do conteúdo de uma matriz pode ser
feito por índice ou com um loop For. Por exemplo:
array := ["one", "two", "three"]
; Iterar de
1 até o final da matriz:
; Enumerar o
conteúdo da matriz:
For index,
value in
array
MsgBox
% "Item " index " is '" value "'"
Uma matriz associativa é um objeto que contém uma coleção de
chaves exclusivas e uma coleção de valores, onde cada chave é associada a um
valor. As chaves podem ser strings, inteiros ou objetos, enquanto os valores
podem ser de qualquer tipo. Uma matriz associativa pode ser criada da seguinte
forma:
Array :=
{KeyA: ValueA, KeyB: ValueB, ..., KeyZ: ValueZ}
Array := Object("KeyA",
ValueA, "KeyB", ValueB, ..., "KeyZ", ValueZ)
Usando a notação {chave: valor}, as marcas de cotação
são opcionais para chaves que consistem apenas em caracteres de palavras.
Qualquer expressão pode ser usada como chave, mas para usar uma variável como
chave, ela deve estar entre parênteses. Por exemplo, {(KeyVar): Value} e
{GetKey (): Value} são ambos válidos.
Recuperar um
item:
Value :=
Array[Key]
Atribuir um
item:
Array[Key]
:= Value
Remover um
item:
RemovedValue
:= Array.Delete(Key)
Enumerando
itens:
array :=
{ten: 10, twenty: 20, thirty: 30}
Matrizes associativas podem ser pouco preenchidas - ou
seja, {1: "a", 1000: "b"} contém apenas dois pares de
valores-chave, não 1000.
No AutoHotkey v1.x, matrizes simples e matrizes
associativas são a mesma coisa. No entanto, tratar [] como uma matriz linear
simples ajuda a manter sua função clara e melhora a chance de seu script
trabalhar com uma versão futura do AutoHotkey, que pode diferenciar entre
matrizes simples e matrizes associativas.
Objetos
[v1.0.90 +]
Para todos os tipos de objetos, a notação
Object.LiteralKey pode ser usada para acessar uma propriedade, elemento de
matriz ou método, onde LiteralKey é um identificador ou inteiro e Object é
qualquer expressão. Identificadores são strings sem aspas, que podem consistir
em caracteres alfanuméricos, sublinhados e, em [v1.1.09 +], caracteres
não-ASCII. Por exemplo, match.Pos é equivalente a match ["Pos"]
enquanto arr.1 é equivalente a arr [1]. Não deve haver espaço após o ponto.
Exemplos:
Recuperar uma propriedade:
Value :=
Object.Property
Defina uma propriedade:
Object.Property
:= Value
Chame um método:
ReturnValue
:= Object.Method(Parameters)
Chame um método com um nome de método computado:
ReturnValue
:= Object[MethodName](Parameters)
Algumas propriedades de objetos COM e objetos
definidos pelo usuário podem aceitar parâmetros:
Value :=
Object.Property[Parameters]
Object.Property[Parameters]
:= Value
Limitação
conhecida:
·
Atualmente, x.y [z] () é tratado como x
["y", z] (), o que não é suportado. Como solução alternativa, (x.y)
[z] () avalia x.y primeiro e, em seguida, usa o resultado como o destino da
chamada do método. Note que x.y [z] .Call () não tem esta limitação, uma vez
que é avaliada da mesma forma que (x.y [z]).
Liberando
Objetos
·
Os scripts não liberam objetos
explicitamente. Quando a última referência a um objeto é liberado, o objeto é
liberado automaticamente. Uma referência armazenada em uma variável é liberada
automaticamente quando essa variável é atribuída a algum outro valor. Por
exemplo:
obj := {}
; Cria um objeto.
obj := "" ; Libera a última referência e,
portanto, libera o objeto.
Da mesma forma, uma referência
armazenada em um campo de outro objeto é liberada quando esse campo é atribuído
a algum outro valor ou removido do objeto. Isso também se aplica a matrizes,
que são objetos.
arr := [{}]
; Creates Cria uma matriz contendo um objeto.
arr[1] := {}
; Cria um segundo objeto, liberando implicitamente o
primeiro objeto.
arr.RemoveAt(1)
; Remove e libera o segundo objeto.
Como todas as referências a um
objeto devem ser liberadas antes que o objeto possa ser liberado, os objetos
que contêm referências circulares não são liberados automaticamente. Por
exemplo, se x.child se refere a y e y.parent se refere a x, a limpeza de x e y
não é suficiente, pois o objeto pai ainda contém uma referência ao filho e
vice-versa. Para resolver essa situação, remova a referência circular.
x := {}, y := {} ; Crie dois objetos.
x.child := y, y.parent := x ; Crie uma referência circular.
y.parent := "" ; A referência
circular deve ser removida antes que os objetos possam ser liberados.
x := "", y := "" ; Sem a linha acima,
isso não libertaria os objetos.
Para uso e detalhes mais avançados, consulte
Contagem
de Referência.
Observações
Sintaxe
Todos os tipos de objetos suportam sintaxe de matriz
(colchetes) e sintaxe de objeto (pontos).
Além disso, referências de objetos podem ser usadas em
expressões:
• Quando uma referência de objeto é comparada com algum
outro valor usando =, ==,! = Ou <>, eles são considerados iguais somente
se ambos os valores forem referências ao mesmo objeto.
• Objetos sempre são considerados verdadeiros quando um
valor booleano é requerido, como em if obj, obj ou obj? x: y.
• O endereço de um objeto pode ser recuperado usando o
operador & address-of. Isso identifica exclusivamente o objeto desde o
ponto de sua criação até o momento em que sua última referência é liberada.
Se um objeto é usado em qualquer contexto em que um objeto
não é esperado, ele é tratado como uma string vazia. Por exemplo, MsgBox%
object% mostra uma MsgBox vazia e o objeto + 1 produz uma string vazia. Não
confie nesse comportamento, pois ele pode mudar.
Quando uma chamada de método é seguida imediatamente por um
operador de atribuição, é equivalente a definir uma propriedade com parâmetros.
Por exemplo, os seguintes são equivalentes:
obj.item(x)
:= y
obj.item[x]
:= y
Atribuições compostas como x.y +
= 1 e --arr [1] são suportadas.
Chaves
Algumas limitações se aplicam a quais valores podem ser
usados como chaves em objetos criados com [], {} ou com o novo operador:
• Chaves inteiras são armazenadas usando o tipo inteiro
assinado nativo. O AutoHotkey de 32 bits oferece suporte a chaves de número
inteiro no intervalo de -2147483648 a 2147483647. O AutoHotkey oferece suporte
a números inteiros de 64 bits, mas apenas o AutoHotkey de 64 bits suporta o
intervalo completo como chaves em um objeto.
• Como consequência do ponto acima, o formato da string de
valores inteiros não é retido. Por exemplo, x [0x10], x [16] e x [00016] são
equivalentes. Isso também se aplica a seqüências numéricas que não têm um ponto
decimal.
• Cadeias literais citadas são consideradas puramente não
numéricas em v1.x, então x [1] e x ["1"] não são equivalentes. Além
disso, se uma cadeia literal entre aspas for concatenada com outro valor (como
em "0x" x), o resultado é tratado como puramente não numérico. No
entanto, isso não se aplica a variáveis, então x [1] e x [y: = "1"]
são equivalentes. Esse problema será resolvido no AutoHotkey v2, portanto, os
scripts devem evitar o uso de literais numéricos citados como chaves.
• Números de ponto flutuante não são suportados como chaves
- em vez disso, eles são convertidos em seqüências de caracteres. Em v1.x,
literais de ponto flutuante mantêm seu formato original enquanto números de
ponto flutuante puros (como o resultado de 0 + 1.0 ou Sqrt (y)) são forçados
para o formato atual de flutuação. Para consistência e clareza, os scripts
devem evitar o uso de literais de ponto flutuante como chaves.
• Por padrão, a chave de string "base" corresponde
à propriedade base do objeto, portanto, não pode ser usada para armazenar
valores comuns com uma atribuição normal. No entanto, qualquer propriedade pode
ser substituída armazenando um valor por algum outro meio, como ObjRawSet
(Object, "base", "") ou Object.SetCapacity
("base", 0). Uma vez feito isso, a chave "base" age como
qualquer outra string.
• Embora os nomes de métodos internos, como
"Comprimento", possam ser usados como chaves, o armazenamento de um
valor impedirá método correspondente de ser chamado (a menos que esse valor
seja uma referência à função apropriada, como ObjLength).
Uso
estendido
Se a
variável func contiver um nome de função, a função pode ser chamada de uma das
duas maneiras:% func% () ou func. (). No entanto, isso requer que o nome da
função seja resolvido a cada vez, o que é ineficiente se a função for chamada
mais de uma vez. Para melhorar o desempenho, o script pode recuperar uma
referência à função e armazená-la para uso posterior:
Uma função pode ser chamada por
referência usando a seguinte sintaxe:
RetVal :=
Func.(Params) ; Not
recommended
Para
detalhes sobre propriedades adicionais de referências de função, consulte Func
Object.
Matrizes de Matrizes
O
AutoHotkey suporta matrizes "multidimensionais", armazenando
transparentemente matrizes dentro de outras matrizes. Por exemplo, uma tabela
pode ser representada como uma matriz de linhas, onde cada linha é uma matriz
de colunas. Nesse caso, o conteúdo da coluna y da linha x pode ser definido
usando um dos métodos abaixo:
table[x][y]
:= content ; A
table[x, y]
:= content ; B
Se a tabela [x] não existe, A e B diferem de duas maneiras:
• A falha enquanto B cria automaticamente um objeto e o
armazena na tabela [x].
• Se a base da tabela definir meta- funções, elas serão
chamadas da seguinte maneira:
table.base.__Get(table, x)[y] := conteúdo ; A
table.base.__Set(table,
x, y, conteúdo) ; B
Conseqüentemente, B permite que o objeto defina o
comportamento personalizado para a atribuição geral.
Atribuições multidimensionais, como tabela [a, b, c,
d]: = value, são tratadas da seguinte maneira:
• Se houver apenas uma chave restante, execute a
atribuição e retorne. De outra forma:
• Procure o objeto pela primeira chave na lista.
• Se um não objeto for encontrado, falhe.
• Se um objeto não for encontrado, crie um e
armazene-o.
• Invoque recursivamente o subobjeto, passando as
chaves restantes e valor - repita a partir do topo.
Esse comportamento se aplica somente a objetos criados
por script, não tipos mais especializados de objetos, como objetos COM ou
matrizes COM.
Matrizes
de Funções
Uma matriz de funções é simplesmente uma matriz
contendo nomes de função ou referências. Por exemplo:
array := [Func("FirstFunc"),
Func("SecondFunc")]
; Chame cada função, passando "foo" como
parâmetro:
; Chame cada função, passando implicitamente o
array como um parâmetro:
FirstFunc(param)
{
}
SecondFunc(param)
{
}
Para compatibilidade com versões anteriores, o
segundo formulário não transmitirá matriz como parâmetro se array [A_Index]
contiver um nome de função em vez de uma referência de função. No entanto, se
array [A_Index] é herdado de array.base [A_Index], array será passado como um
parâmetro.
Objetos
Personalizados
Objetos
criados pelo script não precisam ter nenhuma estrutura predefinida. Em vez
disso, cada objeto pode herdar propriedades e métodos de seu objeto base
(também conhecido como "protótipo" ou "classe").
Propriedades e métodos também podem ser adicionados (ou removidos) de um objeto
a qualquer momento, e essas alterações afetarão todos os objetos derivados.
Para situações mais complexas ou especializadas, um objeto base pode substituir
o comportamento padrão de qualquer objeto derivado dele definindo meta funções.
Objetos
base são apenas objetos comuns e são geralmente criados de duas maneiras:
}
; OR
baseObject
:= {foo: "bar"}
Para criar um objeto derivado de
outro objeto, os scripts podem ser atribuídos à propriedade base ou usar a nova
palavra-chave:
obj1 := Object(),
obj1.base := baseObject
obj2 :=
{base: baseObject}
obj3 := new
baseObject
MsgBox % obj1.foo
" " obj2.foo " " obj3.foo
É possível reatribuir a base de um objeto a
qualquer momento, substituindo efetivamente todas as propriedades e métodos
herdados pelo objeto.
Protótipos
Objetos de protótipo ou base são
construídos e manipulados da mesma forma que qualquer outro objeto. Por
exemplo, um objeto comum com uma propriedade e um método pode ser construído
assim:
; Crie um objeto.
thing := {}
; Armazene um valor.
thing.foo :=
"bar"
; Crie um método armazenando uma referência de
função.
thing.test
:= Func("thing_test")
; Chame o método.
thing.test()
thing_test(this)
{
}
Quando thing.test () é chamado, o
item é inserido automaticamente no início da lista de parâmetros. No entanto,
para compatibilidade com versões anteriores, isso não ocorre quando uma função
é armazenada pelo nome (em vez de por referência) diretamente no objeto (em vez
de ser herdada de um objeto base). Por convenção, a função é nomeada combinando
o "tipo" de objeto e o nome do método.
Um objeto é um protótipo ou base
se outro objeto deriva dele:
other := {}
other.base
:= thing
other.test()
Neste caso, outros herdam foo e testam de coisa.
Este inhe ritance é dinâmico, então se thing.foo for modificado, a mudança será
refletida por other.foo. Se o script é atribuído a other.foo, o valor é
armazenado em outro e quaisquer outras alterações em thing.foo não terão efeito
em other.foo. Quando other.test () é chamado, este parâmetro contém uma
referência a outro em vez de uma coisa.
Em sua raiz, uma "classe" é um conjunto
ou categoria de coisas que têm alguma propriedade ou atributo em comum. Como um
objeto base ou protótipo define propriedades e comportamento para um conjunto
de objetos, ele também pode ser chamado de objeto de classe. Por conveniência,
os objetos base podem ser definidos usando a palavra-chave "class",
conforme mostrado abaixo:
{
InstanceVar := Expression
{
...
}
Method()
{
...
}
Property[]
; Os
suportes são opcionais
{
get {
}
set {
}
}
}
Como a classe é referenciada por
meio de uma variável, o nome da classe não pode ser usado para referenciar a
classe e criar uma variável separada (por exemplo, para manter uma instância da
classe) no mesmo contexto. Por exemplo, box: = new Box substituiria o objeto de
classe no Box por uma instância de si mesmo. [v1.1.27 +]: #Warn ClassOverwrite
permite que um aviso seja exibido no momento do carregamento para cada
tentativa de sobrescrever uma classe.
Dentro desta documentação, a
palavra "classe" geralmente significa um objeto de classe construído
com a palavra-chave class.
As definições de classe podem
conter declarações de variáveis, definições de métodos e definições de classes
aninhadas.
Uma variável de instância é uma
que cada instância da classe (ou seja, cada objeto derivado da classe) possui
sua própria cópia. Eles são declarados como atribuições normais, mas o
presente. Prefixo é omitido (somente diretamente dentro do corpo da classe):
InstanceVar
:= Expression
Essas
declarações são avaliadas sempre que uma nova instância da classe é criada com
a nova palavra-chave. O nome do método __Init é reservado para essa finalidade
e não deve ser usado pelo script. O método __New () é chamado após todas essas
declarações terem sido avaliadas, incluindo aquelas definidas nas classes base.
A expressão pode acessar outras variáveis e métodos de instância por meio
disso, mas todas as outras referências de variáveis são consideradas globais.
Para
acessar uma variável de instância (mesmo dentro de um método), sempre
especifique o objeto de destino; por exemplo, this.InstanceVar.
[v1.1.08+]: Declarações como x.y: = z também
são suportadas, desde que x tenha sido declarado anteriormente nesta classe.
Por exemplo, x: = {}, x.y: = 42 declara x e também inicializa this.x.y.
Variáveis estáticas / de classe pertencem à
própria classe, mas podem ser herdadas por objetos derivados (incluindo
subclasses). Eles são declarados como variáveis de instância, mas usando a
palavra-chave estática:
As declarações estáticas são
avaliadas apenas uma vez, antes da seção de execução automática, na ordem em
que aparecem no script. Cada declaração armazena um valor no objeto de classe.
Qualquer referência de variável na expressão é considerada global.
Para atribuir a uma variável de
classe, sempre especifique o objeto de classe; Por exemplo, ClassName.ClassVar:
= Value. Se um objeto x é derivado de ClassName e x em si não contém a chave
"ClassVar", x.ClassVar também pode ser usado para recuperar
dinamicamente o valor de ClassName.ClassVar. No entanto, x.ClassVar: = y
armazenaria o valor em x, não em ClassName.
Classes
aninhadas
As
definições de classe aninhadas permitem que um objeto de classe seja armazenado
dentro de outro objeto de classe, em vez de uma variável global separada. No
exemplo acima, a classe NestedClass constrói um objeto e armazena-o em
ClassName.NestedClass. As subclasses poderiam herdar NestedClass ou
substituí-la por sua própria classe aninhada (neste caso, new this.NestedClass
poderia ser usado para instanciar qualquer classe apropriada).
{
...
}
Métodos
Definições de método parecem
idênticas às definições de função. Cada método tem um parâmetro oculto chamado
this, que geralmente contém uma referência a um objeto derivado da classe. No
entanto, ele pode conter uma referência à própria classe ou a uma classe
derivada, dependendo sobre como o método foi chamado. Os métodos são
armazenados por referência no objeto de classe.
Method()
{
...
}
Dentro de
um método, a base de pseudo-palavra-chave pode ser usada para acessar as
superclasses de métodos ou propriedades que são substituídas em uma classe
derivada. Por exemplo, base.Method () na classe definida acima chamaria a
versão do método que é definida por BaseClassName. Meta-funções não são
chamadas; caso contrário, base.Method () se comporta como
BaseClassName.Method.Call (this). Isso é,
•
base.Method () sempre invoca a base da classe onde o método atual foi definido,
mesmo que seja derivado de uma subclasse daquela classe ou de alguma outra
classe inteiramente.
•
base.Method () implicitamente passa isso como o primeiro parâmetro (oculto).
base só
tem significado especial se seguido por um ponto. ou colchetes [], então código
como obj: = base, obj.Method () não funcionará. Os scripts podem desabilitar o
comportamento especial da base, atribuindo a ele um valor não vazio; Contudo,
isto não é recomendado. Como a base variável deve estar vazia, o desempenho
pode ser reduzido se o script omitir #NoEnv.
Definições
de propriedade permitem que um método seja executado sempre que o script obtém
ou define uma chave específica.
Property[]
{
get {
}
set {
}
}
Parâmetros
podem ser passados colocando-os entre colchetes à direita do nome da propriedade,
ambos ao definir a propriedade e ao chamá-la. Além do uso de colchetes, os
parâmetros das propriedades são definidos da mesma forma que os parâmetros dos
métodos - os parâmetros opcional, ByRef e variadic são suportados.
O
valor de retorno de get ou set se torna o resultado da subexpressão que chamou
a propriedade. Por exemplo, val: = obj.Property: = 42 armazena o valor de
retorno de set in val.
Cada
classe pode definir uma ou ambas as metades de uma propriedade. Se uma classe
substituir uma propriedade, ela poderá usar base.Property para acessar a
propriedade definida por sua classe base. Se get ou set não estiver definido,
ele pode ser manipulado por uma classe base. Se o conjunto não estiver definido
e não for manipulado por uma meta função ou classe base, a atribuição de um
valor o armazena no objeto, desabilitando efetivamente a propriedade.
Internamente,
get e set são dois métodos separados, portanto, não é possível compartilhar
variáveis (exceto armazená-las).
Meta-funções
fornecem uma maneira mais ampla de controlar o acesso a propriedades e métodos
de um objeto, mas são mais complicadas e propensas a erros.
Construção
e Destruição
Sempre que um objeto derivado é
criado com a nova palavra-chave [requer v1.1.00 +], o método __Novo definido
por seu objeto base é chamado. Esse método pode aceitar parâmetros, inicializar
o objeto e substituir o resultado do novo operador retornando um valor. Quando
um objeto é destruído, __Delete é chamado. Por exemplo:
m1 := new
GMem(0, 20)
m2 := {base:
GMem}.__New(0, 30)
{
__New(aFlags, aSize)
{
this.ptr := DllCall("GlobalAlloc",
"UInt", aFlags, "Ptr", aSize, "Ptr")
MsgBox
% "New GMem of " aSize " bytes at address " this.ptr
"."
return
this ; Esta linha pode ser
omitida ao usar o operador 'new'.
}
__Delete()
{
MsgBox
% "Delete GMem at address " this.ptr "."
DllCall("GlobalFree",
"Ptr", this.ptr)
}
}
__ Delete não é chamado para nenhum objeto
que tenha a chave "__Class".
Class
objects têm essa chave por padrão.
[v1.1.28+]: Se uma exceção ou erro de runtime
for lançado enquanto __Delete estiver sendo executado e não for tratado dentro
de __Delete, ele age como se __Delete fosse chamado de um novo thread. Ou seja,
um diálogo de erro é exibido e __Delete retorna, mas o segmento não sai (a
menos que já esteja saindo). Antes da v1.1.28, as exceções não tratadas
causavam um comportamento inconsistente.
Meta-Funções
Sintaxe do método:
class ClassName
{
__Get([Key, Key2, ...])
__Set([Key, Key2, ...], Value)
__Call(Name [, Params...])
}
Sintaxe da função:
MyGet(this [,
Key, Key2, ...])
MySet(this [,
Key, Key2, ...], Value)
MyCall(this, Name
[, Params...])
ClassName := { __Get:
Func("MyGet"), __Set: Func("MySet"), __Call:
Func("MyCall") }
S
Meta-funções
definem o que acontece quando uma chave é solicitada, mas não encontrada dentro
do objeto alvo. Por exemplo, se obj.key não tiver sido atribuído um valor, ele
chama a meta função __Get. Da mesma forma, obj.key: = value invoca __Set e
obj.key () invoca __Call. Essas meta- funções (ou métodos) precisariam ser
definidas em obj.base, obj.base.base ou algo parecido.
Quando o
script obtém, define ou chama uma chave que não existe no objeto de destino, o
objeto base é chamado da seguinte maneira:
• Se este
objeto base definir a meta função apropriada, chame-o. Se a meta-função
retornar explicitamente, use o retorno valor como o resultado da operação (o
que fez com que a função meta seja chamada) e retorne o controle para o script.
Caso contrário, continue conforme descrito abaixo.
Set: Se a
meta função tratou de uma atribuição, ela deve retornar o valor que foi
atribuído. Isso permite que as atribuições sejam encadeadas, como em a.x: =
b.y: = z. O valor de retorno pode diferir do valor original de z (por exemplo,
se forem impostas restrições sobre quais valores podem ser atribuídos).
•
Procurar por uma chave correspondente nos próprios campos do objeto base.
•
[v1.1.16 +]: Se uma chave correspondente a uma propriedade for encontrada e
implementar get ou set (conforme apropriado), invoque a propriedade e retorne.
Se esta for uma chamada de método, chame get.
• Se
nenhuma chave foi encontrada, invoque recursivamente a própria base desse
objeto base (aplique cada uma dessas etapas a ele, começando no topo desta
lista). Se ainda não tivermos terminado, procure este objeto base por uma chave
correspondente novamente, caso um tenha sido adicionado por uma meta função.
Devido à
compatibilidade com versões anteriores, esta etapa é executada para operações
de conjunto mesmo se uma chave for encontrada (a menos que defina uma
propriedade que implementa o conjunto).
• Se vários
parâmetros foram dados para get ou set e uma chave foi encontrada, verifique
seu valor. Se esse valor for um objeto, manipule os parâmetros restantes
invocando-o e não faça mais nada.
• Se uma
chave foi encontrada,
Get:
Retorna o valor.
Chamar:
tenta chamar o valor, passando o objeto de destino como o primeiro parâmetro
(this). O valor deve ser um nome de função ou um objeto de função.
Se uma
meta função armazena uma chave correspondente no objeto, mas não retorna, o
comportamento é o mesmo que se a chave existisse inicialmente no objeto. Para
obter um exemplo usando __Set, consulte Subclasses Arrays of Arrays.
Se a
operação ainda não foi tratada, verifique se este é um método ou uma
propriedade interna:
• Get: Se
a chave for "base", retorne a base do objeto.
• Set: Se
a chave for "base", defina a base do objeto (ou remova-a se o valor
não for um objeto).
• Ligar:
ligue para um método integrado, se aplicável.
Se a
operação ainda não foi tratada,
• Obter e
ligar: retorna uma string vazia.
• Set: Se
apenas um parâmetro de chave for fornecido, armazene a chave e o valor no
objeto de destino e retorne o valor atribuído. Se vários parâmetros foram
fornecidos, crie um novo objeto e armazene-o usando o primeiro parâmetro como
uma chave e, em seguida, manipule os parâmetros restantes chamando o novo
objeto. (Veja Arrays of Arrays.)
Limitação
conhecida:
• Usar retorno sem um valor é equivalente a
retornar "". Isso pode ser alterado em uma versão futura para que o
retorno possa ser usado para "escapar" de uma meta função sem
substituir o comportamento padrão.
Propriedades dinâmicas
A sintaxe da propriedade pode ser usada para definir
propriedades que calculam um valor toda vez que são avaliadas, mas cada
propriedade deve ser conhecida antecipadamente e definida individualmente no
script. Por outro lado, __Get e __Set podem ser usados para implementar
propriedades que não são conhecidas pelo script.
Por exemplo, um objeto "proxy" poderia ser criado
para enviar solicitações de propriedades pela rede (ou por algum outro canal).
Um servidor remoto enviaria de volta uma resposta contendo o valor da
propriedade, e o proxy retornaria o valor ao seu chamador. Mesmo se o nome de
cada propriedade fosse conhecido antecipadamente, não seria lógico definir cada
propriedade individualmente na classe de proxy, pois cada propriedade faz a
mesma coisa (enviar uma solicitação de rede). Meta-funções recebem o nome da
propriedade como um parâmetro, portanto, são uma boa solução para esse
problema.
Outro uso de __Get e __Set é implementar um conjunto de
propriedades relacionadas que compartilham código. No exemplo abaixo, eles são
usados para implementar um objeto "Color" com propriedades R, G, B
e RGB, onde apenas o valor RGB é realmente armazenado:
red := new Color(0xff0000), red.R -= 5
cyan := new
Color(0), cyan.G := 255, cyan.B := 255
MsgBox %
"red: " red.R "," red.G "," red.B " = "
red.RGB
MsgBox %
"cyan: " cyan.R "," cyan.G "," cyan.B " =
" cyan.RGB
{
__New(aRGB)
{
this.RGB := aRGB
}
static
Shift := {R:16, G:8, B:0}
__Get(aName)
{
; NOTA: Usando isto. Turno aqui causaria um loop
infinito!
shift := Color.Shift[aName] ; Obter o número de bits para mudar.
if
(shift != "") ; É uma propriedade conhecida?
return
(this.RGB >> shift) & 0xff
;
NOTA: Usar 'return' aqui iria quebrar isso.RGB.
}
__Set(aName, aValue)
{
if
((shift := Color.Shift[aName]) != "")
{
aValue &= 255 ; Truncate it to the proper range.
; Calcule e armazene o novo valor RGB.
this.RGB := (aValue << shift)
| (this.RGB & ~(0xff << shift))
; 'Return' deve ser usado para indicar que um novo par de
valores-chave não deve ser criado.
;
Isso também define o que será armazenado no 'x' em 'x: = clr [name]: = val':
}
;
NOTA: Usar 'return' aqui iria quebrar this.stored_RGB e this.RGB.
}
; Meta-funções
podem ser misturadas com propriedades:
RGB {
get {
; Devolva-o no formato hexadecimal:
}
set {
return
this.stored_RGB := value
}
}
}
No entanto, neste caso, a sintaxe da propriedade
poderia ter sido usada em vez disso, onde o código é compartilhado simplesmente
por cada propriedade chamar um método central. É melhor evitar usar
meta-funções sempre que possível devido ao alto risco de uso indevido (veja as
notas em vermelho acima).
Objetos
como Funções
Para um resumo de como criar
objetos que podem atuar como funções, consulte Objetos de Função.
Um objeto de função também pode
atuar como meta-função, como definir propriedades dinâmicas semelhantes às da
seção anterior. Embora seja recomendado usar a sintaxe da propriedade, o
exemplo abaixo mostra o potencial de meta- funções para implementar novos
conceitos ou comportamento, ou alterar a estrutura do script.
blue := new
Color(0x0000ff)
MsgBox % blue.R
"," blue.G "," blue.B
{
Call(aTarget, aName, aParams*)
{
;
Se este objeto Properties contiver uma definição para essa meia-propriedade,
chame-o.
return
this[aName].Call(aTarget, aParams*)
}
}
{
__New(aRGB)
{
this.RGB := aRGB
}
{
R() {
}
G() {
}
B() {
}
}
;...
}
Classes
de matrizes de subclasse
Quando
uma atribuição de vários parâmetros, como tabela [x, y]: = conteúdo
implicitamente faz com que um novo objeto seja criado, o novo objeto
normalmente não tem base e, portanto, nenhum método personalizado ou
comportamento especial. __Set pode ser usado para inicializar esses objetos,
como demonstrado abaixo.
x := {base:
{addr: Func("x_Addr"),
__Set: Func("x_Setter")}}
; Atribuir valor, implicitamente chamando x_Setter para
criar sub-objetos.
x[1,2,3] :=
"..."
; Recuperar valor e método de exemplo de chamada.
MsgBox % x[1,2,3]
"`n" x.addr() "`n" x[1].addr() "`n" x[1,2].addr()
x_Setter(x,
p1, p2, p3) {
x[p1] := new x.base
}
x_Addr(x) {
}
Como x_Setter possui quatro parâmetros
obrigatórios, ele só será chamado quando houver dois ou mais parâmetros-chave.
Quando a atribuição acima ocorre, ocorre o seguinte:
• x [1] não existe, então x_Setter (x, 1,2,3) é
chamado ("..." não é passado, pois há poucos parâmetros).
o x [1] é atribuído um novo objeto com a mesma base
que x.
o Nenhum valor é retornado - a atribuição continua.
• x [1] [2] não existe, então x_Setter (x [1], 2,3,
"...") é chamado.
o x [1] [2] é atribuído um novo objeto com a mesma
base que x [1].
o Nenhum valor é retornado - a atribuição continua.
• x [1] [2] [3] não existe, mas como x_Setter
requer quatro parâmetros e há apenas três (x [1] [2], 3, "..."), ele
não é chamado ea atribuição conclui normalmente.
Objeto
Base Padrão
Quando um valor não objeto é usado com a sintaxe do
objeto, o objeto base padrão é chamado. Isso pode ser usado para depuração ou
para definir globalmente o comportamento de objetos para strings, números e /
ou variáveis. A base padrão pode ser acessada usando .base com qualquer valor
não objeto; por exemplo, "" .base. Embora a base padrão não possa ser
definida como "" .base: = Object (), a base padrão pode ter uma base
como em "" .base.base: = Object ().
Inicialização
Var Automática
Quando
uma variável vazia é usada como destino de uma operação de conjunto, ela é
passada diretamente para a meta função __Set, dando a oportunidade de inserir
um novo objeto na variável. Por uma questão de brevidade, este exemplo não
suporta vários parâmetros; poderia, usando uma função variadic.
"".base.__Set
:= Func("Default_Set_AutomaticVarInit")
empty_var.foo
:= "bar"
Default_Set_AutomaticVarInit(ByRef
var, key, value)
{
}
Pseudo-propriedades
Objeto "açúcar de
sintaxe" pode ser aplicado a seqüências de caracteres e números.
"".base.__Get
:= Func("Default_Get_PseudoProperty")
"".base.is := Func("Default_is")
Default_Get_PseudoProperty(nonobj,
key)
{
}
Default_is(nonobj,
type)
{
}
Note que funções embutidas também
podem ser usadas, mas neste caso os parênteses não podem ser omitidos:
"".base.length
:= Func("StrLen")
Depuração
Se permitir que um valor seja
tratado como um objeto é indesejável, um aviso poderá ser mostrado sempre que
um valor não objeto for invocado:
"".base.__Get
:= "".base.__Set := "".base.__Call := Func("Default__Warn")
empty_var.foo
:= "bar"
x := (1 +
1).is("integer")
Default__Warn(nonobj,
p1="", p2="", p3="", p4="")
{
MsgBox
Um valor não objeto foi invocado incorretamente
`n`nSpecifically: %nonobj%
}
Implementação
Reference-Counting
O
AutoHotkey usa um mecanismo básico de contagem de referências para liberar
automaticamente os recursos usados por um objeto quando não é mais
referenciado pelo script. Autores de script não devem invocar este mecanismo
explicitamente, exceto quando lidando diretamente com pointers
to objects.
Atualmente
no AutoHotkey v1.1, as referências temporárias criadas dentro de uma expressão
(mas não armazenadas em qualquer lugar) são liberadas imediatamente após o uso.
Por exemplo, Fn (& {}) transmite um endereço inválido para a função, porque
a referência temporária retornada por {} é liberada imediatamente após o
operador address-of ser avaliado.
Para
executar o código quando a última referência a um objeto está sendo liberada,
implemente a meta função __Delete.
Limitações
Conhecidas:
• Referências circulares devem ser quebradas antes
que um objeto possa ser liberado. Para detalhes e um exemplo, consulte
Liberando Objetos.
• Embora as referências nas variáveis estáticas e
globais sejam liberadas automaticamente quando o programa é encerrado, as
referências em variáveis locais não estáticas ou na pilha de avaliação de
expressões não são. Essas referências são liberadas somente se a função ou
expressão puder ser concluída normalmente.
Embora a memória usada pelo objeto seja recuperada
pelo sistema operacional quando o programa é encerrado, __Delete não será
chamado a menos que todas as referências ao objeto sejam liberadas. Isso pode
ser importante se liberar outros recursos que não são recuperados
automaticamente pelo sistema operacional, como arquivos temporários.
Ponteiros para Objetos
Em alguns casos raros, pode ser
necessário passar um objeto para código externo via DllCall ou armazená-lo em
uma estrutura de dados binários para recuperação posterior. O endereço de um
objeto pode ser recuperado pelo endereço: = & object; no entanto, isso
efetivamente faz duas referências ao objeto, mas o programa só sabe sobre o
objeto em questão. Se a última referência conhecida ao objeto foi liberada, o
objeto seria excluído. Portanto, o script deve informar ao objeto que ele
ganhou uma referência. Existem duas maneiras de fazer isso:
; Método 1: incrementar explicitamente
a contagem de referência.
address :=
&object
; Método # 2: Use Object (), que incrementa a contagem de
referência e retorna um endereço.
Esta função também pode ser usada
para converter um endereço de volta em uma referência:
De qualquer forma, o script
também deve informar o objeto quando terminar a referência:
; Decrementar a contagem de
referência do objeto para permitir que ele seja liberado:
Geralmente, cada nova cópia do
endereço de um objeto deve ser tratada como outra referência ao objeto,
portanto, o script deve chamar ObjAddRef quando obtiver uma cópia e ObjRelease
imediatamente antes de perder uma. Por exemplo, sempre que um endereço é
copiado por meio de algo como x: = address, ObjAddRef deve ser chamado. Da
mesma forma, quando o script é finalizado com x (ou está prestes a sobrescrever
o valor de x), ele deve chamar ObjRelease.
Observe
que a função Object () pode ser usada até mesmo em objetos que ele não criou,
como COM
objects and File
objects.