sábado, 15 de junho de 2019

Autohotkey - Objects #2 (Tradução) (Pt-Br)

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.
·         Object Protocol: detalhes sobre como um script interage com um objeto.
IsObject pode ser usado para determinar se um valor é um objeto:
Result := IsObject(expression)
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

Simple Arrays [v1.1.21+]

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:

Loop % array.Length()
    MsgBox % array[A_Index]

; Enumerar o conteúdo da matriz:

For index, value in array
    MsgBox % "Item " index " is '" value "'"

Matrizes associativas [v1.1.21+]
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}
For key, value in array
    MsgBox %key% = %value%

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.
[v1.1.20+]: Parâmetros podem ser omitidos ao obter ou configurar propriedades. Por exemplo, x [, 2]. Os scripts podem utilizar isso definindo valores padrão para parâmetros em propriedades e meta-funções. O nome do método também pode ser completamente omitido, como em x [] (a). Os scripts podem utilizar isso definindo um valor padrão para o primeiro parâmetro da função __Call, já que não é fornecido com um valor. Note que isso difere de x (a), que é equivalente a x [""] (a). Se a propriedade ou o nome do método for omitido ao invocar um objeto COM, seu "membro padrão" será invocado.
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

Referências de Funções [v1.1.00+]
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:
Func := Func("MyFunc")

Uma função pode ser chamada por referência usando a seguinte sintaxe:

RetVal := %Func%(Params)     ; Requires [v1.1.07+]
RetVal := Func.Call(Params)  ; Requires [v1.1.19+]
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:

Loop 2
    array[A_Index].Call("foo")

; Chame cada função, passando implicitamente o array como um parâmetro:

Loop 2
    array[A_Index]()

FirstFunc(param) {
    MsgBox % A_ThisFunc ": " (IsObject(param) ? "object" : param)
}
SecondFunc(param) {
    MsgBox % A_ThisFunc ": " (IsObject(param) ? "object" : 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:
class baseObject {
    static foo := "bar"
}
; 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) {
    MsgBox % this.foo
}

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.

Classes [v1.1.00+]
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:
class ClassName extends BaseClassName
{
    InstanceVar := Expression
    static ClassVar := Expression

    class NestedClass
    {
        ...
    }

    Method()
    {
        ...
    }

    Property[]  ; Os suportes são opcionais
    {
        get {
            return ...
        }
        set {
            return ... := value
        }
    }
}

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.


Variáveis ​​de instância [v1.1.01+]
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 [v1.1.00.01+]
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:
static ClassVar := Expression

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).
class NestedClass
{
    ...
}

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.

Propriedade [v1.1.16+]
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 {
        return ...
    }
    set {
        return ... := value
    }
}

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)

class GMem
{
    __New(aFlags, aSize)
    {
        this.ptr := DllCall("GlobalAlloc", "UInt", aFlags, "Ptr", aSize, "Ptr")
        if !this.ptr
            return ""
        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

class Color
{
    __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':
            return aValue
        }
        ; 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:
            return format("0x{:06x}", this.stored_RGB)
        }
        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

class Properties extends FunctionObject
{
    Call(aTarget, aName, aParams*)
    {
        ; Se este objeto Properties contiver uma definição para essa meia-propriedade, chame-o.
        if ObjHasKey(this, aName)
            return this[aName].Call(aTarget, aParams*)
    }
}

class Color
{
    __New(aRGB)
    {
        this.RGB := aRGB
    }

    class __Get extends Properties
    {
        R() {
            return (this.RGB >> 16) & 255
        }
        G() {
            return (this.RGB >> 8) & 255
        }
        B() {
            return this.RGB & 255
        }
    }

    ;...
}

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) {
    return &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"
MsgBox % empty_var.foo

Default_Set_AutomaticVarInit(ByRef var, key, value)
{
    if (var = "")
        var := Object(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")

MsgBox % A_AhkPath.length " == " StrLen(A_AhkPath)
MsgBox % A_AhkPath.length.is("integer")

Default_Get_PseudoProperty(nonobj, key)
{
    if (key = "length")
        return StrLen(nonobj)
}

Default_is(nonobj, type)
{
    if nonobj is %type%
        return true
    return false
}

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")
MsgBox % A_AhkPath.length() " == " StrLen(A_AhkPath)

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="")
{
    ListLines
    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
ObjAddRef(address)

; Método # 2: Use Object (), que incrementa a contagem de referência e retorna um endereço.
address := Object(object)

Esta função também pode ser usada para converter um endereço de volta em uma referência:

object := Object(address)

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:

ObjRelease(address)

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.
Copyright © 2003-2019 - LIC: GNU GPLv2