ভবিষ্যদ্বাণীতে সি # এর পরিবর্তনশীল পুনরায় ব্যবহার করার কোনও কারণ আছে?


1684

সি # তে ল্যাম্বদা এক্সপ্রেশন বা বেনামে পদ্ধতিগুলি ব্যবহার করার সময়, আমাদের সংশোধিত ক্লোজার ফলফেসের অ্যাক্সেস সম্পর্কে সতর্ক থাকতে হবে । উদাহরণ স্বরূপ:

foreach (var s in strings)
{
   query = query.Where(i => i.Prop == s); // access to modified closure
   ...
}

পরিবর্তিত বন্ধের কারণে, উপরের কোডটি Whereকোয়েরির সমস্ত ধারাটির চূড়ান্ত মানের উপর ভিত্তি করে তৈরি করবে s

এখানে বর্ণিত হিসাবে , এটি ঘটে কারণ উপরের লুপে sঘোষিত ভেরিয়েবলটি সংকলকটিতে foreachএভাবে অনুবাদ করা হয়েছে:

string s;
while (enumerator.MoveNext())
{
   s = enumerator.Current;
   ...
}

পরিবর্তে এটি পছন্দ:

while (enumerator.MoveNext())
{
   string s;
   s = enumerator.Current;
   ...
}

যেমন এখানে উল্লেখ করা হয়েছে , লুপের বাইরে ভেরিয়েবল ঘোষণার কোনও কার্যকারিতা সুবিধা নেই এবং সাধারণ পরিস্থিতিতে আমি এটি করার জন্য কেবলমাত্র কারণটি মনে করতে পারি কারণ যদি আপনি লুপের আওতার বাইরে ভেরিয়েবলটি ব্যবহার করার পরিকল্পনা করেন:

string s;
while (enumerator.MoveNext())
{
   s = enumerator.Current;
   ...
}
var finalString = s;

তবে একটি foreachলুপে সংজ্ঞায়িত ভেরিয়েবলগুলি লুপের বাইরে ব্যবহার করা যায় না:

foreach(string s in strings)
{
}
var finalString = s; // won't work: you're outside the scope.

সুতরাং সংকলকটি এমনভাবে ভেরিয়েবলটি ঘোষণা করে যা কোনও ত্রুটিযুক্ত প্রবণতায় পরিণত করে যা প্রায়শই খুঁজে পাওয়া এবং ডিবাগ করা কঠিন, যখন কোনও উপলব্ধিযোগ্য লাভ না করে।

foreachলুপগুলি নিয়ে আপনি এমনভাবে কিছু করতে পারেন যা আপনি যদি কোনও অভ্যন্তরীণ-স্কোপযুক্ত ভেরিয়েবল সংকলন করেন তবে আপনি এটি করতে পারতেন না বা এটি কেবল একটি স্বেচ্ছাসেবী পছন্দ যা বেনাম পদ্ধতি এবং ল্যাম্বডা এক্সপ্রেশন উপলব্ধ বা প্রচলিত হওয়ার আগে তৈরি হয়েছিল এবং যা কোন অভ্যাস ছিল না এর পর থেকে সংশোধন করা হচ্ছে না?


4
এর সাথে কী হয়েছে String s; foreach (s in strings) { ... }?
ব্র্যাড ক্রিস্টি

5
@ ব্র্যাডক্রিস্টি ওপি সত্যিই কথা বলছে না foreachবরং লামদা এক্সপ্রেশন নিয়ে কথা বলছে যার ফলশ্রুতিতে ওপি দেখায় একই রকম কোড ...
ইয়াহিয়া

22
@ ব্র্যাডক্রিশি: এটি কি সংকলন করে? ( ত্রুটি: টাইপ এবং শনাক্তকারী উভয়ই আমার জন্য পূর্বানুমানের বিবৃতিতে প্রয়োজনীয় )
অস্টিন সালোনেন

32
@ জ্যাকববটস নিলসন: এটি ল্যাম্বদার একটি বহিরাগত বহিরাগত লোকাল; আপনি কেন ধরে নিচ্ছেন যে এটি একেবারে স্ট্যাকের মধ্যে চলেছে? এটি স্ট্যাক ফ্রেমের চেয়ে দীর্ঘকালীন !
এরিক লিপার্ট

3
@ এরিকলিপার্ট: আমি বিভ্রান্ত আমি বুঝতে পারি যে ল্যাম্বদা ফোরচ ভেরিয়েবলের একটি রেফারেন্স ক্যাপচার করে (যা অভ্যন্তরীণভাবে লুপের বাইরে ঘোষিত হয়) এবং সুতরাং আপনি এর চূড়ান্ত মানটির সাথে তুলনা করে শেষ করেন; যে আমি পেয়েছি। আমি যা বুঝতে পারি না তা হ'ল লুপের অভ্যন্তরে ভেরিয়েবল কীভাবে ঘোষণার ফলে কোনও পার্থক্য হবে। সংকলক-লেখকের দৃষ্টিকোণ থেকে ঘোষণাটি লুপের ভিতরে বা বাইরে রয়েছে তা নির্বিশেষে আমি কেবল স্ট্যাকের উপর একটি স্ট্রিং রেফারেন্স (var 's') বরাদ্দ করছি; আমি অবশ্যই প্রতিটি পুনরুক্তি স্ট্যাকের উপর একটি নতুন রেফারেন্স ঠেলাতে চাই না!
অ্যান্টনি

উত্তর:


1407

সংকলকটি এমনভাবে ভেরিয়েবলটি ঘোষণা করে যা কোনও ত্রুটিযুক্ত প্রবণতায় পরিণত করে যা প্রায়শই খুঁজে পাওয়া এবং ডিবাগ করা শক্ত হয়, যখন কোনও উপলব্ধিযোগ্য লাভ না করে।

আপনার সমালোচনা সম্পূর্ণ ন্যায়সঙ্গত।

আমি এখানে এই সমস্যাটি বিস্তারিতভাবে আলোচনা করি:

লুপ ভেরিয়েবলটিকে ক্ষতিকারক বলে মনে করে বন্ধ করা

ফোরচ লুপগুলির সাথে আপনি এমন কিছু করতে পারেন যা আপনি যদি কোনও অভ্যন্তরীণ-স্কোপযুক্ত ভেরিয়েবলের সাথে সংকলন করে থাকেন তবে আপনি পারতেন না? বা এটি কি কেবল একটি স্বেচ্ছাসেবী পছন্দ যা বেনাম পদ্ধতি এবং ল্যাম্বডা মত প্রকাশের আগে পাওয়া গিয়েছিল বা সাধারণ ছিল এবং এর পরে কোনটি সংশোধন করা হয়নি?

পরেরটি। সি # 1.0 স্পেসিফিকেশন আসলে লুপ ভেরিয়েবলটি লুপের দেহের অভ্যন্তরে বা বাইরে ছিল কিনা তা বলে নি, কারণ এটি কোনও পর্যবেক্ষণযোগ্য পার্থক্য রাখেনি। সি # ২.০-এ ক্লোজার সিমানটিকস চালু করা হলে, "ফর" লুপের সাথে সামঞ্জস্য রেখে লুপের বাইরে লুপ পরিবর্তনশীল রাখার জন্য পছন্দ করা হয়েছিল।

আমি মনে করি এটা ঠিক যে এই সিদ্ধান্তের জন্য সমস্ত আফসোস করেছেন। এটি সি # এর মধ্যে সবচেয়ে খারাপ "গ্যাটাচস" এর মধ্যে একটি এবং এটি সমাধানের জন্য আমরা ব্রেকিং পরিবর্তন নিতে যাচ্ছি। সি # 5 এ ফোরচ লুপ ভেরিয়েবলটি লজিকভাবে লুপের মূল অংশের অভ্যন্তরে থাকবে এবং সুতরাং ক্লোজারগুলি প্রতিবার একটি নতুন কপি পাবে।

forলুপ পরিবর্তন করা যাবে না, এবং পরিবর্তন হবে না C # এর পূর্ববর্তী সংস্করণ "ফিরে বৈশিষ্ট্যসমূহ নিয়ে আসা"। সুতরাং এই আইডিয়ামটি ব্যবহার করার সময় আপনার সতর্কতা অবলম্বন করা উচিত।


177
আমরা আসলে সি # 3 এবং সি # 4 এর পরিবর্তনের দিকে পিছনে চাপ দিয়েছি যখন আমরা সি # 3 ডিজাইন করেছি তখন আমরা বুঝতে পেরেছিলাম যে সমস্যাটি (যা ইতিমধ্যে সি # 2 তে বিদ্যমান ছিল) আরও খারাপ হতে চলেছে কারণ সেখানে অনেকগুলি ল্যাম্বডাস থাকবে (এবং ক্যোয়ারী লিনিউকে ধন্যবাদ ফোরচ লুপগুলিতে অনুধাবন, যা ছদ্মবেশে ল্যাম্বডাস হয়। আমি দুঃখিত যে আমরা সমস্যাটি সি # 3 এ ফিক্সিংয়ের চেয়ে এত দেরি করে ফিক্সিংয়ের পক্ষে যথেষ্ট খারাপ হওয়ার জন্য অপেক্ষা করছিলাম
এরিক লিপার্ট

75
এবং এখন আমাদের মনে রাখতে হবে foreachএটি 'নিরাপদ' তবে forতা নয়।
লেপি

22
@ মিচিয়েলভু: পরিবর্তনটি এই অর্থে ভেঙে যাচ্ছে যে এটি পশ্চাদপটে সামঞ্জস্যপূর্ণ নয়। পুরানো সংকলক ব্যবহার করার সময় নতুন কোড সঠিকভাবে চলবে না।
লেপি

41
@ বেঞ্জল: না, তাই আমরা এটি নিতে ইচ্ছুক। জন স্কিট আমাকে একটি গুরুত্বপূর্ণ ব্রেকিং চেঞ্জের দৃশ্যের দিকে ইঙ্গিত করেছিলেন যদিও এটি হ'ল যে কেউ সি # 5 তে কোড লিখেছেন, এটি পরীক্ষা করে এবং তারপরে এটি এমন লোকদের সাথে ভাগ করে নেন যারা এখনও সি # 4 ব্যবহার করছেন, যারা নির্লজ্জভাবে এটিকে সঠিক বলে বিশ্বাস করেন। আশা করি এ জাতীয় দৃশ্যে আক্রান্ত মানুষের সংখ্যা অল্পই।
এরিক লিপার্ট

29
একদিকে যেমন রিশার্পার সর্বদা এটি ধরা পড়েছে এবং এটি "পরিবর্তিত বন্ধের অ্যাক্সেস" হিসাবে রিপোর্ট করেছে। তারপরে, Alt + Enter টিপে, এটি স্বয়ংক্রিয়ভাবে আপনার কোডটিও ঠিক করে দেবে। jetbrains.com/resharper
মাইক চেম্বারলাইন

191

আপনি যা জিজ্ঞাসা করছেন তা এরিক লিপার্ট তার ব্লগ পোস্টে ক্ষতিকারক এবং এর সিক্যুয়াল হিসাবে বিবেচিত লুপ ভেরিয়েবলের উপর বন্ধ করে পুঙ্খানুপুঙ্খভাবে কভার করেছেন ।

আমার জন্য, সবচেয়ে দৃ argument়প্রত্যয়ী যুক্তি হ'ল প্রতিটি পুনরাবৃত্তিতে নতুন পরিবর্তনশীল থাকা for(;;)স্টাইল লুপের সাথে সঙ্গতিপূর্ণ নয় । আপনি কি int iপ্রতিটি পুনরাবৃত্তিতে একটি নতুন থাকার আশা করবেন for (int i = 0; i < 10; i++)?

এই আচরণের সাথে সর্বাধিক সাধারণ সমস্যাটি পুনরাবৃত্তকরণের পরিবর্তনশীলকে বন্ধ করে দেওয়া এবং এটিতে একটি সহজ কাজ রয়েছে:

foreach (var s in strings)
{
    var s_for_closure = s;
    query = query.Where(i => i.Prop == s_for_closure); // access to modified closure

এই সমস্যাটি সম্পর্কে আমার ব্লগ পোস্ট: সি # তে ফরচ ভেরিয়েবলের উপর বন্ধ


18
শেষ পর্যন্ত, লোকেরা এটি লেখার সময় আসলে কী চায় তা একাধিক ভেরিয়েবলের নয়, এটির মানটি বন্ধ করে দেওয়া । এবং সাধারণ ক্ষেত্রে এটির জন্য ব্যবহারযোগ্য বাক্য গঠন সম্পর্কে ভাবা শক্ত।
র্যান্ডম 832

1
হ্যাঁ, মান দ্বারা বন্ধ করা সম্ভব নয়, তবে একটি খুব সহজ কাজ রয়েছে যা আমি যুক্ত করতে আমার উত্তরটি সম্পাদনা করেছি।
ক্রিজ

6
রেফারেন্সের তুলনায় এটি সি # তে খুব খারাপ বন্ধ রয়েছে। যদি সেগুলি ডিফল্টরূপে মানগুলি বন্ধ করে দেয় তবে আমরা সহজেই এর পরিবর্তে ভেরিয়েবলগুলির মাধ্যমে ক্লোজিং নির্দিষ্ট করতে পারি ref
শন ইউ

2
@ ক্রিজ, এটি এমন একটি ক্ষেত্রে যেখানে বাধ্যতামূলক ধারাবাহিকতা অসঙ্গত হওয়ার চেয়ে বেশি ক্ষতিকারক। লোকেদের যেমন প্রত্যাশা করা হয় তেমনি এটি "ঠিক কাজ" করা উচিত এবং স্পষ্টভাবে লোকে ফোরের বিপরীতে ফোরচ ব্যবহার করার সময় লোকেদের আলাদা কিছু প্রত্যাশা করা উচিত, পরিবর্তিত ইস্যুতে অ্যাক্সেস সম্পর্কে আমাদের জানার আগে সমস্যার মুখোমুখি হওয়া লোকদের সংখ্যা (যেমন আমার মতো) ।
অ্যান্ডি

2
@ র্যান্ডম 832 সি # সম্পর্কে জানেন না তবে সাধারণ এলআইএসপিতে এর জন্য একটি বাক্য গঠন রয়েছে এবং এটি পরিসংখ্যান করে যে পরিবর্তনীয় ভেরিয়েবল এবং ক্লোজারগুলির সাথে কোনও ভাষাও এটি (না, অবশ্যই ) থাকতে পারে। আমরা হয় পরিবর্তিত স্থানের রেফারেন্সের সাথে বন্ধ করে দিই, অথবা একটি নির্দিষ্ট মুহুর্তে এটির একটি মান সময়ের সাথে (বন্ধের সৃষ্টি)। এই (পাইথন এবং প্রকল্প অনুরূপ কাপড় আলোচনা cutrefs জন্য / Vars এবং cuteরাখার জন্য মূল্যায়ন আংশিক মূল্যায়ন বন্ধ মান)।
নেস

103

এটি দ্বারা দংশিত হওয়ার পরে, আমার অভ্যন্তরীণতম স্কোপগুলিতে স্থানীয়ভাবে সংজ্ঞায়িত ভেরিয়েবলগুলি অন্তর্ভুক্ত করার অভ্যাস রয়েছে যা আমি কোনও বন্ধে স্থানান্তর করতে ব্যবহার করি। আপনার উদাহরণে:

foreach (var s in strings)
    query = query.Where(i => i.Prop == s); // access to modified closure

আমি করি:

foreach (var s in strings)
{
    string search = s;
    query = query.Where(i => i.Prop == search); // New definition ensures unique per iteration.
}        

আপনার যদি এই অভ্যাসটি হয়ে যায় তবে আপনি খুব বিরল ক্ষেত্রে এটি এড়াতে পারবেন যা আপনি বাস্তবে বাইরের স্কোপগুলিতে আবদ্ধ হওয়ার ইচ্ছা করেছিলেন। সত্যি কথা বলতে কি আমি কখনই এমনটি করিনি বলে মনে করি না।


24
অবদানের জন্য এটি সাধারণ কাজের জন্য ধন্যবাদ। এই প্যাটার্নটি সনাক্ত করতে এবং এটিও আপনার নজরে আনার জন্য রিশার্পার যথেষ্ট স্মার্ট which এটি দুর্দান্ত। আমি কিছুক্ষণের মধ্যে এই প্যাটার্নটি দ্বারা কিছুটা হলেও হইনি, তবে যেহেতু এটি, এরিক লিপার্টের ভাষায়, "আমরা প্রাপ্ত একক সাধারণ ভুল বাগ রিপোর্ট," এটিকে কীভাবে এড়ানো যায় তার চেয়ে কেন আমি আরও জানতে আগ্রহী ছিলাম ।
স্ট্রিপলিং ওয়ারিয়র

62

সি # 5.0 এ, এই সমস্যাটি স্থির হয়ে গেছে এবং আপনি লুপ ভেরিয়েবলগুলি বন্ধ করতে পারেন এবং আপনার প্রত্যাশিত ফলাফলগুলি পেতে পারেন।

ভাষার স্পেসিফিকেশন বলে:

8.8.4 পূর্বাভাস বিবৃতি

(...)

ফর্ম একটি foreach বিবৃতি

foreach (V v in x) embedded-statement

এরপরে প্রসারিত:

{
  E e = ((C)(x)).GetEnumerator();
  try {
      while (e.MoveNext()) {
          V v = (V)(T)e.Current;
          embedded-statement
      }
  }
  finally {
       // Dispose e
  }
}

(...)

vএম্বেড-বিবৃতিতে ঘটে যাওয়া কোনও বেনামি কার্যক্রমে কীভাবে এটি ক্যাপচার করা হয় তার জন্য ওয়েল লুপের ভিতরে স্থাপন গুরুত্বপূর্ণ। উদাহরণ স্বরূপ:

int[] values = { 7, 9, 13 };
Action f = null;
foreach (var value in values)
{
    if (f == null) f = () => Console.WriteLine("First value: " + value);
}
f();

যদি ওয়েল vলুপের বাইরে ঘোষণা করা হয়, এটি সমস্ত পুনরাবৃত্তির মধ্যে ভাগ করা হবে এবং লুপের পরে এর মানটি চূড়ান্ত মান হবে 13, যা অনুরোধটি fমুদ্রণ করবে। পরিবর্তে, যেহেতু প্রতিটি পুনরাবৃত্তির নিজস্ব ভেরিয়েবল রয়েছে v, fপ্রথম পুনরাবৃত্তির মাধ্যমে ধরা পড়াটির মান ধরে রাখা চলবে 7, যা মুদ্রিত হবে। ( দ্রষ্টব্য: সি # এর পূর্ববর্তী সংস্করণগুলি vলুপের বাইরে ঘোষিত হয়েছে ))


1
সি # এর এই প্রাথমিক সংস্করণটি কেন লুপের অভ্যন্তরে v ঘোষিত? msdn.microsoft.com/en-GB/library/aa664754.aspx
কলিনফ্যাং

4
@ কোলিনফ্যাং এরিকের উত্তরটি অবশ্যই পড়ুন : সি # 1.0 স্পেসিফিকেশন ( আপনার লিঙ্কে আমরা ভিএস 2003 এর কথা বলছি, অর্থাৎ সি # 1.2 ) আসলে লুপের পরিবর্তনশীলটি লুপের দেহের ভিতরে বা বাইরে ছিল কিনা তা বলতে পারেনি , কারণ এটি কোনও পর্যবেক্ষণযোগ্য পার্থক্য রাখে না । সি # ২.০-এ ক্লোজার সিমানটিকস চালু করা হলে, "ফর" লুপের সাথে সামঞ্জস্য রেখে লুপের বাইরে লুপ পরিবর্তনশীল রাখার জন্য পছন্দ করা হয়েছিল।
পাওলো মোরেত্তি

1
সুতরাং আপনি কি বলছেন যে লিঙ্কটিতে উদাহরণগুলি নির্দিষ্ট সময়ে নির্দিষ্ট ছিল না?
কলিনফ্যাং

4
@colinfang- এ তারা ছিল সুনির্দিষ্ট নির্দিষ্টকরণ। সমস্যাটি হ'ল আমরা এমন একটি বৈশিষ্ট্য (অর্থাৎ ফাংশন ক্লোজার) সম্পর্কে কথা বলছি যা পরে চালু হয়েছিল (সি # ২.০ সহ)। সি # 2.0 এলে তারা লুপের বাইরে লুপের পরিবর্তনশীল রাখার সিদ্ধান্ত নিয়েছে। এবং তার তুলনায় তারা আবার সি # 5.0 :) দিয়ে তাদের মন পরিবর্তন করেছেন
পাওলো মোরেটি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.