Saltar para o conteúdo

Sombreamento variável

Origem: Wikipédia, a enciclopédia livre.

Na programação de computadores, o sombreamento de variáveis ocorre quando uma variável declarada em um determinado escopo tem o mesmo nome de uma variável declarada em um escopo externo (nível superior ao atual). No nível dos identificadores, isso é conhecido como mascaramento de nomes. Quando isso ocorre, diz-se que esta variável externa é sombreada pela variável interna, enquanto o identificador interno mascara o identificador externo. Isso pode causar confusão na leitura, por poder não estar claro a qual variável os usos subsequentes do nome da variável sombreada se referem, o que depende das regras de resolução de nomes da linguagem.

O sombreamento de variáveis foi introduzido inicialmente em ALGOL, uma das primeiras linguagens a empregar blocos para definir escopos. Esse conceito foi adotado por muitas outras linguagens de programação que se originaram dela, como C, C++ e Java.

A linguagem C# desvia dessa tradição ao permitir o sombreamento de variáveis entre uma classe interna e uma classe externa, e também entre um método e a classe que o contém. No entanto, não permite o sombreamento entre um bloco if e o método que o contém, nem entre instruções case em um bloco switch.

Há variações na permissão de sombreamento de variáveis entre as linguagens. Por exemplo, Kotlin permite que uma variável interna em uma função oculte um argumento passado e uma variável em um bloco interno oculte a variável do bloco externo, enquanto Java não permite essa flexibilidade. No entanto, ambas as linguagens permitem que um argumento passado para uma função ou método oculte um campo de classe. [1]

Algumas linguagens não permitem completamente o sombreamento de variáveis, como CoffeeScript . [2]

O código Lua a seguir fornece um exemplo de sombreamento de variável, onde v é declarado em múltiplas instâncias.

v = 1 -- declara v como variavel global

do
 local v = v + 1 -- declara um novo v local que sombreia a variavel global
                 -- mas v'segundo' = v'global' + 1  
 print(v) -- imprime 2

 do
  local v = v * 2 -- novamente é declarado uma nova variavel com mesmo nome 
                  -- v'terceiro' = v'segundo' * 2
  print(v) -- imprime 4
 end

 print(v) -- imprime 2
end

print(v) -- imprime 1

A seguir um exemplo de sombreamento de variável em Python:

# Atribui um valor à variável no escopo global
x = 0

def outer():
  # Atribui um valor à variável no escopo da função outer
  x = 1

  def inner():
    # Atribui um valor à variável no escopo da função inner
    x = 2
    # Imprime x no escopo da função inner: saída "inner: 2"
    print("inner:", x)

  inner()
  # imprime x no escopo da função outer: saída "outer: 1"
  print("outer:", x)

outer()
# imprime x no escopo global: saída "global: 0"
print("global:", x)

Em Python, onde não há uma declaração explícita de variável, mas sim apenas atribuição, a palavra-chave nonlocal, introduzida em Python3, é utilizada para evitar a ocorrência de sombreamento de variáveis e permitir a atribuição a variáveis em escopos externos ao local atual de execução.

# Atribui um valor à variável no escopo global
x = 0

def outer():
  # Atribui um valor à variável no escopo da função outer
  x = 1

  def inner():
    # Acessa a variável x no escopo de outer, escopo anterior
    nonlocal x
    # Atribui um novo valor à variável x no escopo de outer
    x = 2
    # Imprime x no escopo da função inner saída: "inner: 2"
    print("inner:", x)

  inner()
  # Imprime x no escopo da função outer saída: "outer: 2"
  print("outer:", x)

outer()
# Imprime x no escopo global saída: "global: 0"
print("global:", x)

A palavra-chave global é usada para evitar o sombreamento de variáveis e acessa a atribuição globais:

# Atribui um valor à variável no escopo global
x = 0

def outer():
  # Atribui um valor à variável no escopo da função outer
  x = 1

  def inner():
    # Permite acessar e atribuir a variável global x
    global x
    # Atribui um novo valor à variável global x
    x = 2
    # Imprime x no escopo da função inner saída: "inner: 2"
    print("inner:", x)

  inner()
  # Imprime x no escopo da função outer saída: "outer: 1"
  print("outer:", x)

outer()
# Imprime x no escopo global saída: "global: 1"
print("global:", x)
fn main() {
  let x = 0;    // Atribui um valor à variável no escopo principal
  
  {
    // Sombreamento
    let x = 1;  // Atribui um novo valor à variável no escopo interno
    println!("Inner x: {}", x); // Imprime x no escopo interno saída: "Inner x: 1"
  }
  
  println!("Outer x: {}", x); // Imprime x no escopo externo saída: "Outer x: 0"
  
  let x = "Rust";   // Atribui um novo valor à variável no escopo principal
  println!("Outer x: {}", x); // Imprime x no escopo externos saída: "Outer x: Rust"
}
#include <iostream>

int main()
{
 int x = 42;    // Atribui um valor à variável x  no escopo principal
 int sum = 0;   // Atribui um valor à variável sum no escopo principal

 for (int i = 0; i < 10; i++) {
  int x = i;    // Atribui um novo valor à variável x no escopo do loop
  std::cout << "x: " << x << '\n'; // Imprime os valores de x de 0 a 9
  sum += x;     // Atualiza a variável sum com o valor de x 
 }

 std::cout << "sum: " << sum << '\n';// Imprime a soma dos valores de x : 45
 std::cout << "x:  " << x  << '\n';  // Imprime o valor de x no escopo principal: 42

 return 0;
}
public class Shadow {
  private int myIntVar = 0;  // Atribui um valor a variavel myIntVar

  public void shadowTheVar() {
    // Como tem o mesmo nome que o campo de instância acima, ele sombreia 
    // o campo acima dentro deste método.
    int myIntVar = 5;  // Atribui um novo valor à variável local myIntVar

    // Se simplesmente nos referirmos a 'myIntVar', o valor desta variável local 
    // é encontrado (sombreando uma segunda variável com o mesmo nome)
    System.out.println(myIntVar); // Imprime myIntVar no escopo deste método: 5

    // Se quisermos nos referir ao myIntVar sombreado desta classe, precisamos 
    // nos referir a ele assim:
    System.out.println(this.myIntVar); // Imprime myIntVar sombreado: 0
  }

  public static void main(String[] args){
    new Shadow().shadowTheVar();
  }
}

No ECMAScript 6 de JavaScript as instruções let e const com escopo de bloco permitem o sombreamento de variável.

function myFunc() {
  let my_var = 'test'; // Atribui um valor à variável no escopo da função
  if (true) {
    let my_var = 'new test';  // Atribui um novo valor à variável no escopo do bloco if
    console.log(my_var);  // Imprime my_var no escopo do bloco if: 'new test'
  }
  console.log(my_var); // Imprime my_var no escopo da função: 'test'
}
myFunc();
  1. «From Java to Kotlin and Back Again». Consultado em 4 de outubro de 2021. Arquivado do original em 28 de novembro de 2020 
  2. «Please introduce explicit shadowing · Issue #2697 · jashkenas/Coffeescript». GitHub. Consultado em 4 de outubro de 2021. Arquivado do original em 4 de outubro de 2021