В программировании затенение переменных происходит , когда переменная, объявленная в определенной области действия (блоке принятия решений, методе или внутреннем классе ), имеет то же имя, что и переменная, объявленная во внешней области действия. На уровне идентификаторов (имен, а не переменных) это известно как маскировка имен . Говорят, что эта внешняя переменная затеняется внутренней переменной, в то время как внутренний идентификатор маскирует внешний идентификатор. Это может привести к путанице, поскольку может быть неясно, к какой переменной относятся последующие использования затененного имени переменной, что зависит от правил разрешения имен языка.
Одним из первых языков, введших затенение переменных, был ALGOL , который впервые ввел блоки для установления областей видимости. Это также было разрешено многими производными языками программирования, включая C , C++ и Java .
Язык C# нарушает эту традицию, допуская затенение переменных между внутренним и внешним классом, а также между методом и содержащим его классом, но не между if-блоком и содержащим его методом или между операторами case в блоке switch .
Некоторые языки допускают затенение переменных в большем количестве случаев, чем другие. Например, Kotlin допускает внутреннюю переменную в функции затенять переданный аргумент, а переменную во внутреннем блоке — затенять другую во внешнем блоке, тогда как Java не допускает этого. Оба языка допускают, чтобы переданный аргумент функции/метода затенял поле класса. [1]
Некоторые языки полностью запрещают затенение переменных, например CoffeeScript [2] и V (Vlang) [3] .
Следующий код Lua представляет собой пример затенения переменных в нескольких блоках.
v = 1 — глобальная переменнаяdo local v = v + 1 — новый локальный объект, который затеняет глобальный v print ( v ) — печатает 2 do local v = v * 2 -- другой локальный, который затеняет внешний локальный v print ( v ) -- печатает 4 end print ( v ) — печатает 2 конецпечать ( v ) — печатает 1
Следующий код Python представляет собой еще один пример теневого копирования переменных:
х = 0определение внешнего (): x = 1 def inner (): x = 2 print ( "inner:" , x ) внутренняя () печать ( "внешняя:" , x )внешний () печать ( "глобальный:" , x )# отпечатки # внутренние: 2 # внешние: 1 # глобальные: 0
Поскольку в Python нет объявления переменных, а есть только назначение переменных, ключевое слово, nonlocal
введенное в Python 3, используется для предотвращения затенения переменных и назначения нелокальным переменным:
х = 0определение внешнего (): x = 1 def inner (): нелокальный x x = 2 print ( "inner:" , x ) внутренняя () печать ( "внешняя:" , x )внешний () печать ( "глобальный:" , x )# отпечатки # внутренний: 2 # внешний: 2 # глобальный: 0
Ключевое слово global
используется для предотвращения затенения переменных и назначения их глобальным переменным:
х = 0определение внешнего (): x = 1 def inner (): глобальный x x = 2 print ( "inner:" , x ) внутренняя () печать ( "внешняя:" , x )внешний () печать ( "глобальный:" , x )# отпечатки # внутренние: 2 # внешние: 1 # глобальные: 2
fn main () { let x = 0 ; { // Тень let x = 1 ; println! ( "Внутренний x: {}" , x ); // печатает 1 } println! ( "Внешний x: {}" , x ); // печатает 0 let x = "Rust" ; println! ( "Внешний x: {}" , x ); // печатает 'Rust' } //# Внутренний x: 1 //# Внешний x: 0 //# Внешний x: Ржавчина
#include <iostream> int main () { int x = 42 ; int sum = 0 ; for ( int i = 0 ; i < 10 ; i ++ ) { int x = i ; std :: cout << "x: " << x << '\n' ; // выводит значения i от 0 до 9 sum += x ; } std :: cout << "sum: " << sum << '\n' ; // выводит 45 std :: cout << "x: " << x << '\n' ; // выводит 42 вернуть 0 ; }
открытый класс Shadow { частный int myIntVar = 0 ; public void shadowTheVar () { // Поскольку у него такое же имя, как у поля экземпляра объекта выше, он затеняет // поле выше внутри этого метода. int myIntVar = 5 ; // Если мы просто ссылаемся на 'myIntVar' , то один из этих методов будет найден // (затеняющий второй с тем же именем) System.out.println ( myIntVar ) ; // печатает 5 // Если мы хотим сослаться на затененный myIntVar из этого класса , нам нужно // сослаться на него следующим образом: System.out.println ( this.myIntVar ) ; // выводит 0 } public static void main ( String [] args ) { new Shadow (). shadowTheVar (); } }
Введение ECMAScript 6let
с const
областью действия блока позволяет затенять переменные.
function myFunc () { let my_var = ' test' ; if ( true ) { let my_var = ' new test' ; console.log ( my_var ) ; // новый тест } console.log ( my_var ); // тест } myFunc ( );