উত্তর:
এই বিষয়টি নিয়ে আমার একটি নিবন্ধ রয়েছে । (এর প্রচুর উদাহরণ রয়েছে))
সংক্ষেপে, একটি বন্ধন কোডের একটি ব্লক যা পরবর্তী সময়ে কার্যকর করা যেতে পারে, তবে এটি পরিবেশটি বজায় রাখে যেখানে এটি তৈরি হয়েছিল প্রথমে - অর্থাৎ এটি এখনও পদ্ধতিটি তৈরি করেছে এর স্থানীয় ভেরিয়েবল ইত্যাদি ব্যবহার করতে পারে এমনকি তার পরেও পদ্ধতিটি কার্যকর করা শেষ করেছে।
বন্ধের সাধারণ বৈশিষ্ট্য বেনামে পদ্ধতি এবং ল্যাম্বদা এক্সপ্রেশন দ্বারা সি # তে প্রয়োগ করা হয়।
বেনামে পদ্ধতি ব্যবহার করে এখানে একটি উদাহরণ দেওয়া হয়েছে:
using System;
class Test
{
static void Main()
{
Action action = CreateAction();
action();
action();
}
static Action CreateAction()
{
int counter = 0;
return delegate
{
// Yes, it could be done in one statement;
// but it is clearer like this.
counter++;
Console.WriteLine("counter={0}", counter);
};
}
}
আউটপুট:
counter=1
counter=2
এখানে আমরা দেখতে পাচ্ছি যে ক্রিয়েটএ্যাকশন দ্বারা ফিরে আসা ক্রিয়াকলাপটি এখনও কাউন্টার ভেরিয়েবলটিতে অ্যাক্সেস পেয়েছে এবং প্রকৃতপক্ষে ক্রিয়েটএ্যাকশন শেষ হয়ে গেলেও এটি এটিকে বাড়িয়ে তুলতে পারে।
counter
বর্ধিত হওয়ার জন্য উপলভ্য - সংকলকটি একটি শ্রেণি তৈরি করে যার মধ্যে একটি counter
ক্ষেত্র রয়েছে এবং যে কোনও কোড counter
ends শ্রেণীর উদাহরণ হিসাবে চলে ends
আপনি যদি আগ্রহী হন যে সি # কীভাবে ক্লোজার প্রয়োগ করে "আমি উত্তরটি জানি (এর 42) ব্লগটি"
সংকলক ব্যাকগ্রাউন্ডে anoymous পদ্ধতি এবং ভেরিয়েবল জে encapصل করতে একটি বর্গ তৈরি করে
[CompilerGenerated]
private sealed class <>c__DisplayClass2
{
public <>c__DisplayClass2();
public void <fillFunc>b__0()
{
Console.Write("{0} ", this.j);
}
public int j;
}
কাজের জন্য:
static void fillFunc(int count) {
for (int i = 0; i < count; i++)
{
int j = i;
funcArr[i] = delegate()
{
Console.Write("{0} ", j);
};
}
}
এটিকে রূপান্তর করা:
private static void fillFunc(int count)
{
for (int i = 0; i < count; i++)
{
Program.<>c__DisplayClass1 class1 = new Program.<>c__DisplayClass1();
class1.j = i;
Program.funcArr[i] = new Func(class1.<fillFunc>b__0);
}
}
বন্ধগুলি হ'ল কার্যকরী মান যা তাদের আসল সুযোগ থেকে পরিবর্তনশীল মানগুলিকে ধরে রাখে। সি # এগুলি বেনামে প্রতিনিধিদের আকারে ব্যবহার করতে পারে।
খুব সাধারণ উদাহরণের জন্য, এই সি # কোডটি নিন:
delegate int testDel();
static void Main(string[] args)
{
int foo = 4;
testDel myClosure = delegate()
{
return foo;
};
int bar = myClosure();
}
এটির শেষে, বারটি 4 তে সেট করা হবে এবং মাই ক্লোজার প্রতিনিধিটিকে প্রোগ্রামের অন্য কোথাও ব্যবহার করার জন্য পাস করা যেতে পারে।
বন্ধগুলি কার্যকরভাবে অনেক কাজে ব্যবহার করা যেতে পারে যেমন বিলম্বিত সম্পাদন বা ইন্টারফেস সহজ করার জন্য - লিনকুই মূলত ক্লোজার ব্যবহার করে তৈরি করা হয়। বেশিরভাগ বিকাশকারীদের পক্ষে এটি কার্যকরভাবে আসার উপায়টি ইভেন্ট হ্যান্ডলারগুলিকে গতিশীলভাবে তৈরি করা নিয়ন্ত্রণগুলিতে যুক্ত করছে - আপনি অন্য কোথাও ডেটা সঞ্চয় করার পরিবর্তে নিয়ন্ত্রণটি তাত্ক্ষণিকভাবে চালু করার সময় আচরণ যুক্ত করতে ক্লোজার ব্যবহার করতে পারেন।
Func<int, int> GetMultiplier(int a)
{
return delegate(int b) { return a * b; } ;
}
//...
var fn2 = GetMultiplier(2);
var fn3 = GetMultiplier(3);
Console.WriteLine(fn2(2)); //outputs 4
Console.WriteLine(fn2(3)); //outputs 6
Console.WriteLine(fn3(2)); //outputs 6
Console.WriteLine(fn3(3)); //outputs 9
একটি বন্ধন এটি তৈরি করা ফাংশনের বাইরে পাস করা একটি বেনামি ফাংশন। এটি যে ফাংশনটিতে এটি ব্যবহার করে তা তৈরি হয় যা থেকে কোনও পরিবর্তনশীল বজায় রাখে।
এখানে সি # এর একটি স্বীকৃত উদাহরণ যা আমি জাভাস্ক্রিপ্টে অনুরূপ কোড থেকে তৈরি করেছি:
public delegate T Iterator<T>() where T : class;
public Iterator<T> CreateIterator<T>(IList<T> x) where T : class
{
var i = 0;
return delegate { return (i < x.Count) ? x[i++] : null; };
}
সুতরাং, এখানে কিছু কোড যা দেখায় যে উপরের কোডটি কীভাবে ব্যবহার করতে হয় ...
var iterator = CreateIterator(new string[3] { "Foo", "Bar", "Baz"});
// So, although CreateIterator() has been called and returned, the variable
// "i" within CreateIterator() will live on because of a closure created
// within that method, so that every time the anonymous delegate returned
// from it is called (by calling iterator()) it's value will increment.
string currentString;
currentString = iterator(); // currentString is now "Foo"
currentString = iterator(); // currentString is now "Bar"
currentString = iterator(); // currentString is now "Baz"
currentString = iterator(); // currentString is now null
আশা করি এটি কিছুটা সহায়ক।
মূলত ক্লোজার হ'ল কোডের একটি ব্লক যা আপনি কোনও ফাংশনের আর্গুমেন্ট হিসাবে পাস করতে পারেন। সি # বেনামী প্রতিনিধি আকারে ক্লোজার সমর্থন করে।
এখানে একটি সহজ উদাহরণ:
তালিকা.ফাইন্ড পদ্ধতি তালিকার আইটেমটি সন্ধানের জন্য কোডের টুকরো (ক্লোজার) গ্রহণ এবং সম্পাদন করতে পারে।
// Passing a block of code as a function argument
List<int> ints = new List<int> {1, 2, 3};
ints.Find(delegate(int value) { return value == 1; });
সি # 3.0 সিনট্যাক্স ব্যবহার করে আমরা এটিকে এটি লিখতে পারি:
ints.Find(value => value == 1);
একটি বন্ধন হ'ল যখন কোনও ফাংশন অন্য ফাংশনের (বা পদ্ধতি) ভিতরে সংজ্ঞায়িত হয় এবং এটি প্যারেন্ট পদ্ধতি থেকে ভেরিয়েবল ব্যবহার করে । ভেরিয়েবলের এই ব্যবহার যা কোনও পদ্ধতিতে অবস্থিত এবং এর মধ্যে নির্ধারিত কোনও ফাংশনে আবৃত থাকে, এটি ক্লোজার বলে।
মার্ক সিমেনের ব্লগ পোস্টে ক্লোজারগুলির কয়েকটি আকর্ষণীয় উদাহরণ রয়েছে যেখানে তিনি ওও এবং ফাংশনাল প্রোগ্রামিংয়ের মধ্যে একটি প্যারালিল করেন।
এবং এটি আরও বিশদ করতে
var workingDirectory = new DirectoryInfo(Environment.CurrentDirectory);//when this variable
Func<int, string> read = id =>
{
var path = Path.Combine(workingDirectory.FullName, id + ".txt");//is used inside this function
return File.ReadAllText(path);
};//the entire process is called a closure.
ক্লোজারগুলি এমন একটি কোডের অংশ যা তাদের বাইরে কোনও পরিবর্তনশীলকে বোঝায় (তাদের নীচে থেকে স্ট্যাকের উপরে), যা পরে বলা বা সম্পাদন করা যেতে পারে (যেমন কোনও ইভেন্ট বা প্রতিনিধি সংজ্ঞায়িত হওয়ার পরে এবং ভবিষ্যতে কোনও অনির্দিষ্ট সময়ে ডাকা যেতে পারে) ) ... কারণ বাহ্যিক ভেরিয়েবল যা কোড রেফারেন্সের অংশটি সুযোগের বাইরে চলে যায় (এবং অন্যথায় এটি হারিয়ে যেত), এটি কোডের অংশ দ্বারা (রেফারেন্স বলা হয়) দ্বারা উল্লেখ করা হয়েছে যা রানটাইমকে "ধরে রাখতে বলে" "কোডের ক্লোজার খণ্ডের দ্বারা আর এটির দরকার না হওয়া অবধি সুযোগে এই পরিবর্তনশীল ...
আমি এটিও বোঝার চেষ্টা করছি, নীচে জাভাস্ক্রিপ্ট এবং সি # তে একই কোডের কোড স্নিপেটগুলি বন্ধ দেখানো হচ্ছে।
জাভাস্ক্রিপ্ট:
var c = function ()
{
var d = 0;
function inner() {
d++;
alert(d);
}
return inner;
};
var a = c();
var b = c();
<body>
<input type=button value=call onClick="a()"/>
<input type=button value=call onClick="b()"/>
</body>
সি #:
using System.IO;
using System;
class Program
{
static void Main()
{
var a = new a();
var b = new a();
a.call();
a.call();
a.call();
b.call();
b.call();
b.call();
}
}
public class a {
int b = 0;
public void call()
{
b++;
Console.WriteLine(b);
}
}
javascript:
var c = function ()
{
var d = 0;
function inner() {
d++;
alert(d);
}
return inner;
};
var a = c();
<input type=button value=call onClick="a()"/>
<input type=button value=call onClick="a()"/>
সি #:
using System.IO;
using System;
class Program
{
static void Main()
{
var a = new a();
var b = new a();
a.call();
a.call();
a.call();
b.call();
b.call();
b.call();
}
}
public class a {
static int b = 0;
public void call()
{
b++;
Console.WriteLine(b);
}
}
নীল রঙের ঠিক বাইরে, সি # 7.0 সংক্ষেপে বইয়ের একটি সহজ এবং আরও বোঝার উত্তর।
প্রাক-আবশ্যক আপনার জানা উচিত : একটি ল্যাম্বডা এক্সপ্রেশনটি স্থানীয় ভেরিয়েবল এবং পদ্ধতির প্যারামিটারগুলিকে উল্লেখ করতে পারে যেখানে এটি সংজ্ঞায়িত হয়েছে (বাহ্যিক ভেরিয়েবল)।
static void Main()
{
int factor = 2;
//Here factor is the variable that takes part in lambda expression.
Func<int, int> multiplier = n => n * factor;
Console.WriteLine (multiplier (3)); // 6
}
আসল অংশ : ল্যাম্বডা এক্সপ্রেশন দ্বারা রেফারেন্সযুক্ত আউটার ভেরিয়েবলগুলি ক্যাপচারড ভেরিয়েবলগুলি বলে। একটি ল্যাম্বডা এক্সপ্রেশন যা ভেরিয়েবল ক্যাপচার করে তাকে ক্লোজার বলে।
সর্বশেষ পয়েন্টটি লক্ষণীয় : ক্যাপচারড ভেরিয়েবলগুলি মূল্যায়ন করা হয় যখন ডেলিগেট আসলে অনুরোধ করা হয়, ভেরিয়েবলগুলি কবে ধরা হয়েছিল তা নয়:
int factor = 2;
Func<int, int> multiplier = n => n * factor;
factor = 10;
Console.WriteLine (multiplier (3)); // 30
আপনি যদি কোনও ইনলাইন বেনামে পদ্ধতি (সি # 2) বা (পছন্দ) একটি ল্যাম্বডা এক্সপ্রেশন (সি # 3 +) লিখেন তবে একটি আসল পদ্ধতি এখনও তৈরি হচ্ছে। যদি সেই কোডটি একটি বহিরাগত স্কোপ স্থানীয় ভেরিয়েবল ব্যবহার করে - তবে আপনাকে এখনও সেই পরিবর্তনশীলটি কোনওভাবে পদ্ধতিতে পাস করতে হবে।
উদাহরণস্বরূপ এই লিনকটি নিন যেখানে ক্লজ (এটি একটি সাধারণ এক্সটেনশন পদ্ধতি যা ল্যাম্বডা এক্সপ্রেশনটি পাস করে):
var i = 0;
var items = new List<string>
{
"Hello","World"
};
var filtered = items.Where(x =>
// this is a predicate, i.e. a Func<T, bool> written as a lambda expression
// which is still a method actually being created for you in compile time
{
i++;
return true;
});
আপনি যদি সেই ল্যাম্বদা এক্সপ্রেশনটিতে i ব্যবহার করতে চান তবে আপনাকে এটি তৈরি পদ্ধতিতে পাস করতে হবে।
সুতরাং প্রথম প্রশ্নটি দেখা দেয়: এটি মান বা রেফারেন্স দ্বারা পাস করা উচিত?
রেফারেন্সের মাধ্যমে পাসটি হ'ল (আমি অনুমান করি) আপনি সেই পরিবর্তনশীলটিতে পড়ার / লেখার অ্যাক্সেস পাওয়ার সাথে সাথে আরও ভাল (এবং এটি সি # যা করে; আমি অনুমান করি যে মাইক্রোসফ্টের দলটি উপকারিতা এবং মতামত ওজন করেছে এবং বাই রেফারেন্সের সাথে চলেছে; জোন স্কিটির মতে ) নিবন্ধ , জাভা বাই-ভ্যালু সহ গেল)।
কিন্তু তারপরে আর একটি প্রশ্ন ওঠে: আমি কোথায় বরাদ্দ করব?
এটি আসলে / প্রাকৃতিকভাবে স্ট্যাকের বরাদ্দ করা উচিত? ঠিক আছে, আপনি যদি এটি স্ট্যাকের জন্য বরাদ্দ করেন এবং রেফারেন্সের মাধ্যমে এটি পাস করেন তবে এমন পরিস্থিতি তৈরি হতে পারে যেখানে এটি নিজের স্ট্যাকের ফ্রেমকে ছাড়িয়ে যায়। এই উদাহরণটি ধরুন:
static void Main(string[] args)
{
Outlive();
var list = whereItems.ToList();
Console.ReadLine();
}
static IEnumerable<string> whereItems;
static void Outlive()
{
var i = 0;
var items = new List<string>
{
"Hello","World"
};
whereItems = items.Where(x =>
{
i++;
Console.WriteLine(i);
return true;
});
}
ল্যাম্বডা এক্সপ্রেশন (যেখানে ক্লজটিতে) আবার একটি পদ্ধতি তৈরি করে যা একটি আইকে বোঝায়। যদি আমি আউটলাইভের স্ট্যাকের জন্য বরাদ্দ করা হয়, তবে আপনি যখনই আইটেমগুলি গণনা করবেন, আমি উত্পন্ন পদ্ধতিতে ব্যবহৃত আই আউটলাইভের আইটিকে নির্দেশ করব, অর্থাত্ স্ট্যাকের কোনও জায়গায় যা আর অ্যাক্সেসযোগ্য নয়।
ঠিক আছে, তাই আমাদের তখন এটি স্তূপের প্রয়োজন।
সুতরাং সি # সংকলকটি এই ইনলাইন বেনামে / ল্যাম্বডাকে সমর্থন করতে যা করে, " ক্লোজারস " নামে পরিচিত এটি ব্যবহার করে: এটি হিপ নামে একটি ক্লাস তৈরি করে ( বরং খারাপভাবে ) ডিসপ্লেসক্লাসে আই ক্ষেত্রযুক্ত একটি ক্ষেত্র রয়েছে এবং যে ফাংশনটি আসলে ব্যবহার করে এটা।
এর সমতুল্য কিছু (আপনি ILSpy বা ILDASM ব্যবহার করে উত্পন্ন আইএল দেখতে পারেন):
class <>c_DisplayClass1
{
public int i;
public bool <GetFunc>b__0()
{
this.i++;
Console.WriteLine(i);
return true;
}
}
এটি আপনার স্থানীয় স্কোপে সেই বর্গটি ইনস্ট্যান্ট করে এবং আই বা ল্যাম্বডা এক্সপ্রেশন সম্পর্কিত কোনও কোডকে সেই ক্লোজারের উদাহরণ সহ প্রতিস্থাপন করে। সুতরাং - যে কোনও সময় আপনি আপনার "স্থানীয় স্কোপ" কোডে যেখানে আমি সংজ্ঞায়িত হয়েছিল সেখানে ব্যবহার করছি, আপনি আসলে সেই প্রদর্শনক্লাস উদাহরণ ক্ষেত্রটি ব্যবহার করছেন।
সুতরাং আমি যদি প্রধান পদ্ধতিতে "স্থানীয়" আমি পরিবর্তন করতে পারি, তবে এটি _DisplayClass.i আসলে পরিবর্তন হবে;
অর্থাত
var i = 0;
var items = new List<string>
{
"Hello","World"
};
var filtered = items.Where(x =>
{
i++;
return true;
});
filtered.ToList(); // will enumerate filtered, i = 2
i = 10; // i will be overwriten with 10
filtered.ToList(); // will enumerate filtered again, i = 12
Console.WriteLine(i); // should print out 12
এটি 12 মুদ্রণ করবে, যেহেতু "i = 10" অপ্রত্যাশিত ক্ষেত্রটিতে যায় এবং দ্বিতীয় গণনার ঠিক আগে এটি পরিবর্তন করে।
বিষয়টির একটি ভাল উত্স হ'ল এই বার্ট ডি স্মেট বহুবর্ধক মডিউল (নিবন্ধকরণের প্রয়োজন) (এছাড়াও "হোস্টিং" শব্দটির তার ভ্রান্ত ব্যবহারকে উপেক্ষা করুন - তিনি (আমার মনে হয়) তার অর্থ হ'ল স্থানীয় ভেরিয়েবল (যেমন i) উল্লেখ করার জন্য পরিবর্তন করা হয়েছে নতুন ডিসপ্লেক্লাস ফিল্ডে)।
অন্যান্য খবরে, কিছু ভুল ধারণা রয়েছে বলে মনে হচ্ছে যে "ক্লোজারগুলি" লুপগুলির সাথে সম্পর্কিত - কারণ আমি বুঝতে পারি "বন্ধকরণ" লুপ সম্পর্কিত কোনও ধারণা নয় , বরং বেনামে পদ্ধতিতে / ল্যাম্বডা এক্সপ্রেশনগুলি স্থানীয় স্কোপড ভেরিয়েবলগুলির ব্যবহার - যদিও কিছু কৌশল প্রশ্নগুলি এটি প্রদর্শনের জন্য লুপগুলি ব্যবহার করে।
ক্লোজার হ'ল একটি ফাংশন যা কোনও ফাংশনের মধ্যে সংজ্ঞায়িত হয়, এটি এর স্থানীয় ভেরিয়েবলগুলির পাশাপাশি তার পিতামাতাকে অ্যাক্সেস করতে পারে।
public string GetByName(string name)
{
List<things> theThings = new List<things>();
return theThings.Find<things>(t => t.Name == name)[0];
}
সুতরাং অনুসন্ধান পদ্ধতির ভিতরে ফাংশন।
t => t.Name == name
এর স্কোপ, টি এর মধ্যে ভেরিয়েবলগুলি অ্যাক্সেস করতে পারে এবং তার পিতামাতার স্কোপে থাকা ভেরিয়েবলের নাম। যদিও এটি ডেলিগেট হিসাবে সন্ধানের পদ্ধতি দ্বারা কার্যকর করা হয়, অন্য সুযোগ থেকে সমস্ত একসাথে।