اندازهی قلم متن
تخمین مدت زمان مطالعهی مطلب:
سه دقیقه
Capturing Outer Variables
یک عبارت لامبدا میتواند از متغیرهای محلی و یا پارامترهای متدی که در آن تعریف شده است، استفاده نماید (Outer Variables). این متغیرها را captured variables مینامند. عبارت لامبدایی که از این متغیرها استفاده میکند، closure نامیده میشود. برای مثال:static void Main() { int factor = 2; Func<int, int> multiplier = n => n * factor; Console.WriteLine (multiplier (3)); // 6 }
در کد فوق multiplier یک delegate میباشد که ورودی صحیح n را گرفته و در مقدار factor ضرب کرده و بر میگرداند.
عبارت لامبدا زمانی ارزیابی میشود که delegate متناظر فراخوانی (Invoke) گردد؛ نه زمانیکه متغیر اصطلاحا capture میشود:
int factor = 2; Func<int, int> multiplier = n => n * factor; factor = 10; Console.WriteLine (multiplier (3)); // 30
عبارات لامبدا خود میتوانند captured variableها را تغییر دهند:
int seed = 0; Func<int> natural = () => seed++; Console.WriteLine (natural()); // 0 Console.WriteLine (natural()); // 1 Console.WriteLine (seed); // 2
طول عمر(lifetime) متغیرهای captured شده در حد طول عمر delegate افزایش پیدا میکند. در مثال زیر متغیر محلی seed در حالت معمول، محدوده دیدی (scope) در حد تعریف این متغیر تا پایان اجرای متد دارد. اما از آنجاییکه در اینجا متغیر captured شده است، طول عمر آن در حدا طول عمر delegate افزایش مییابد: theNatural
static Func<int> Natural() { int seed = 0; return () => seed++; // Returns a closure } static void Main() { Func<int> theNatural = Natural(); Console.WriteLine (theNatural ()); // 0 Console.WriteLine (theNatural ()); // 1 }
static Func<int> Natural() { return() => { int seed = 0; return seed++; }; } static void Main() { Func<int> natural = Natural(); Console.WriteLine (natural()); // 0 Console.WriteLine (natural()); // 0 }
نکته: پیاده سازی پروسه Capture شدن متغیر، به این صورت است که این متغیرها به عنوان یک فیلد از یک کلاس (با سطح دسترسی private) در نظر گرفته میشوند. زمانیکه متد فراخوانی شد، کلاس مزبور وهله سازی شده و طول عمر آن به طول عمر delegate گره میخورد.
Capturing iteration variables
در حلقه for، وقتی که متغیر حلقه توسط یک عبارت لامبدا capture میگردد، #C با آن متغیر طوری رفتار میکند که گویی در خارج از حلقه تعریف شدهاست و این بدان معناست که در هر بار تکرار حلقه، مقدار یکسانی برای متغیر در نظر گرفته میشود. کد زیر 333 را در خروجی چاپ میکند(بجای 012).
Action[] actions = new Action[3]; for (int i = 0; i < 3; i++) actions [i] = () => Console.Write (i); foreach (Action a in actions) a(); // 333
با نوشتن کد زیر میتوان درک بهتری از موضوع پیدا کرد.
Action[] actions = new Action[3]; int i = 0; actions[0] = () => Console.Write (i); i = 1; actions[1] = () => Console.Write (i); i = 2; actions[2] = () => Console.Write (i); i = 3; foreach (Action a in actions) a(); // 333
Action[] actions = new Action[3]; for (int i = 0; i < 3; i++) { int loopScopedi = i; actions [i] = () => Console.Write (loopScopedi); } foreach (Action a in actions) a(); // 012