for
বনাম foreach
একটি সাধারণ বিভ্রান্তি রয়েছে যে এই দুটি কনস্ট্রাক্টস খুব একই রকম এবং উভয়ই এর মত বিনিময়যোগ্য:
foreach (var c in collection)
{
DoSomething(c);
}
এবং:
for (var i = 0; i < collection.Count; i++)
{
DoSomething(collection[i]);
}
উভয় কীওয়ার্ড একই তিনটি অক্ষর দ্বারা শুরু হয় তা অর্থহীনভাবে বোঝায় না, সেগুলি একই রকম। এই বিভ্রান্তি অত্যন্ত ত্রুটি-প্রবণ, বিশেষত নতুনদের জন্য। সংগ্রহের মাধ্যমে আইট্রেট করা এবং উপাদানগুলির সাথে কিছু করা এর সাথে করা হয় foreach
; for
আপনি এই কাজটি করছেন তা যদি না জেনে থাকে তবে এই উদ্দেশ্যে ব্যবহার করার দরকার নেই এবং ব্যবহার করা উচিত নয় ।
আসুন দেখুন একটি উদাহরণ সহ এটিতে কী সমস্যা রয়েছে। শেষে, আপনি ফলাফল সংগ্রহের জন্য ব্যবহৃত একটি ডেমো অ্যাপ্লিকেশনটির পুরো কোডটি খুঁজে পাবেন।
উদাহরণস্বরূপ, আমরা "বোস্টন" এর মুখোমুখি হওয়ার আগে, ডেটাবেস থেকে কিছু তথ্য লোড করছি, স্পষ্টভাবে অ্যাডভেঞ্চার ওয়ার্কের শহরগুলি, নাম অনুসারে অর্ডার করা হয়েছে। নিম্নলিখিত এসকিউএল কোয়েরি ব্যবহৃত হয়:
select distinct [City] from [Person].[Address] order by [City]
ডেটা ListCities()
পদ্ধতি দ্বারা লোড করা হয় যা একটি প্রদান করে IEnumerable<string>
। foreach
দেখতে দেখতে যা এখানে :
foreach (var city in Program.ListCities())
{
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
আসুন এটি একটি দিয়ে আবার লিখি for
, ধরে নিই যে উভয়ই বিনিময়যোগ্য:
var cities = Program.ListCities();
for (var i = 0; i < cities.Count(); i++)
{
var city = cities.ElementAt(i);
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
উভয়ই একই শহরে ফিরে আসে, তবে একটি বিশাল পার্থক্য রয়েছে।
- ব্যবহার করার সময়
foreach
, ListCities()
এক সময় বলা হয় এবং 47 টি আইটেম দেয়।
- ব্যবহার করার সময়
for
, ListCities()
এটি 94 বার বলা হয় এবং সামগ্রিকভাবে 28153 আইটেম দেয়।
কি হলো?
IEnumerable
হয় অলস । এর অর্থ হল যে ফলাফলটি যখন প্রয়োজন তখন কেবল এই মুহূর্তে কাজটি করে। অলস মূল্যায়ন একটি খুব দরকারী ধারণা, তবে এর কিছু মুহূর্ত রয়েছে যাতে এই মুহূর্ত (গুলি) মিস করা সহজ যেখানে ফলাফলের প্রয়োজন হবে, বিশেষত এমন ক্ষেত্রে যেখানে ফলাফল একাধিকবার ব্যবহৃত হয়।
একটি ক্ষেত্রে foreach
, ফলাফল একবারেই অনুরোধ করা হয়। একটি একটি যদি for
উপরে ভুল লিখিত কোডে হিসাবে প্রয়োগ করা , ফলে অনুরোধ 94 বার , অর্থাত্ 47 × 2:
একের পরিবর্তে একটি ডাটাবেসকে 94 বার জিজ্ঞাসা করা ভয়ানক, তবে ঘটতে পারে এমন খারাপ জিনিস নয়। উদাহরণস্বরূপ কল্পনা করুন, যদি select
ক্যোয়ারির আগে টেবিলের মধ্যে একটি সারি সন্নিবেশ করানো হয় এমন কোনও ক্যোয়ারির আগে কী হয়। ঠিক আছে, আমাদের কাছে এমনটি রয়েছে for
যা আশা করি আগে ক্র্যাশ না করা হলে ডাটাবেসটি 2,147,483,647 বার কল করবে ।
অবশ্যই, আমার কোড পক্ষপাতদুষ্ট। আমি ইচ্ছাকৃতভাবে আলস্য ব্যবহার করেছি IEnumerable
এবং বারবার কল করার জন্য এটি লিখেছিলাম ListCities()
। যে কেউ সূচনা করতে পারে যে কোনও শিক্ষানবিস কখনই এটি করতে পারে না, কারণ:
দ্য IEnumerable<T>
সম্পত্তি নেই Count
, কিন্তু শুধুমাত্র পদ্ধতি Count()
। কোনও পদ্ধতিকে কল করা ভয়ঙ্কর, এবং কেউ আশা করতে পারে যে এর ফলাফলটি ক্যাশে হবে না এবং কোনও for (; ...; )
ব্লকের পক্ষে উপযুক্ত নয় ।
সূচকের জন্য অনুপলব্ধ IEnumerable<T>
এবং ElementAt
লিনকিউ এক্সটেনশন পদ্ধতিটি খুঁজে পাওয়া সুস্পষ্ট নয় ।
সম্ভবত সবচেয়ে নতুনদের শুধু ফল রূপান্তর হবে ListCities()
কিছু তারা সঙ্গে পরিচিত, একটি মত List<T>
।
var cities = Program.ListCities();
var flushedCities = cities.ToList();
for (var i = 0; i < flushedCities.Count; i++)
{
var city = flushedCities[i];
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
তবুও, এই কোডটি foreach
বিকল্পের থেকে খুব আলাদা । আবার এটি একই ফলাফল দেয় এবং এবার ListCities()
পদ্ধতিটি কেবল একবার বলা হয় তবে 575 টি আইটেম দেয়, যখন foreach
এটি কেবল 47 টি আইটেম দেয়।
পার্থক্যটি এমন তথ্য থেকে আসে যে ToList()
কারণে ডেটাবেস থেকে সমস্ত ডেটা লোড হয়। foreach
"বোস্টন" এর আগে কেবলমাত্র শহরগুলিকে অনুরোধ করার সময়, নতুনটির for
জন্য সমস্ত শহর পুনরুদ্ধার করা এবং মেমরিতে সংরক্ষণ করা দরকার। 575 সংক্ষিপ্ত স্ট্রিং সহ, এটি সম্ভবত খুব বেশি পার্থক্য করে না, তবে আমরা যদি বিলিয়ন রেকর্ডযুক্ত টেবিল থেকে কেবল কয়েকটি সারি উদ্ধার করি?
তাহলে আসলে foreach
কী?
foreach
লুপ কাছাকাছি হয়। আমি আগে যে কোডটি ব্যবহার করেছি:
foreach (var city in Program.ListCities())
{
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
সহজেই প্রতিস্থাপন করা যেতে পারে:
using (var enumerator = Program.ListCities().GetEnumerator())
{
while (enumerator.MoveNext())
{
var city = enumerator.Current;
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
}
উভয়ই একই আইএল উত্পাদন করে। উভয়ের একই ফল রয়েছে। উভয়ের একই পার্শ্ব প্রতিক্রিয়া রয়েছে। অবশ্যই এটি আবার while
একই রকম অসীমতে আবার লেখা যেতে পারে for
তবে এটি আরও দীর্ঘতর এবং ত্রুটি-প্রবণ হবে। আপনি যাকে আরও পাঠযোগ্য বলে বেছে নিতে বেছে নিতে পারেন free
এটি নিজে পরীক্ষা করতে চান? এখানে সম্পূর্ণ কোড:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
public class Program
{
private static int countCalls;
private static int countYieldReturns;
public static void Main()
{
Program.DisplayStatistics("for", Program.UseFor);
Program.DisplayStatistics("for with list", Program.UseForWithList);
Program.DisplayStatistics("while", Program.UseWhile);
Program.DisplayStatistics("foreach", Program.UseForEach);
Console.WriteLine("Press any key to continue...");
Console.ReadKey(true);
}
private static void DisplayStatistics(string name, Action action)
{
Console.WriteLine("--- " + name + " ---");
Program.countCalls = 0;
Program.countYieldReturns = 0;
var measureTime = Stopwatch.StartNew();
action();
measureTime.Stop();
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("The data was called {0} time(s) and yielded {1} item(s) in {2} ms.", Program.countCalls, Program.countYieldReturns, measureTime.ElapsedMilliseconds);
Console.WriteLine();
}
private static void UseFor()
{
var cities = Program.ListCities();
for (var i = 0; i < cities.Count(); i++)
{
var city = cities.ElementAt(i);
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
}
private static void UseForWithList()
{
var cities = Program.ListCities();
var flushedCities = cities.ToList();
for (var i = 0; i < flushedCities.Count; i++)
{
var city = flushedCities[i];
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
}
private static void UseForEach()
{
foreach (var city in Program.ListCities())
{
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
}
private static void UseWhile()
{
using (var enumerator = Program.ListCities().GetEnumerator())
{
while (enumerator.MoveNext())
{
var city = enumerator.Current;
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
}
}
private static IEnumerable<string> ListCities()
{
Program.countCalls++;
using (var connection = new SqlConnection("Data Source=mframe;Initial Catalog=AdventureWorks;Integrated Security=True"))
{
connection.Open();
using (var command = new SqlCommand("select distinct [City] from [Person].[Address] order by [City]", connection))
{
using (var reader = command.ExecuteReader(CommandBehavior.SingleResult))
{
while (reader.Read())
{
Program.countYieldReturns++;
yield return reader["City"].ToString();
}
}
}
}
}
}
এবং ফলাফল:
--- for ---
অ্যাবিংডন আলবানি আলেকজান্দ্রিয়া আলহামব্রা [...] বন বোর্দো বোস্টন
ডেটাটিকে 94 বার বলা হয়েছিল এবং 28153 আইটেম (গুলি) দেওয়া হয়েছিল।
--- তালিকা সহ ---
অ্যাবিংডন আলবানি আলেকজান্দ্রিয়া আলহাম্ব্রা [...] বন বোর্ডো বোস্টন
ডেটাটিকে 1 সময় বলা হয়েছিল এবং 575 টি আইটেম পাওয়া যায় yield
--- যখন ---
অ্যাবিংডন আলবানি আলেকজান্দ্রিয়া আলহাম্ব্রা [...] বন বোর্দো বোস্টন
ডেটাটিকে 1 সময় বলা হয়েছিল এবং 47 টি আইটেম পাওয়া যায়।
--- foreach ---
অ্যাবিংডন আলবানি আলেকজান্দ্রিয়া আলহাম্ব্রা [...] বন বোর্ডো বোস্টন
ডেটাটিকে 1 সময় বলা হয়েছিল এবং 47 টি আইটেম পাওয়া যায়।
লিনিকিউ বনাম traditionalতিহ্যবাহী পদ্ধতি
লিনকিউ হিসাবে, আপনি ক্রিয়ামূলক প্রোগ্রামিং (এফপি) শিখতে চাইতে পারেন - সি # এফপি স্টাফ নয়, তবে হাস্কেলের মতো বাস্তব এফপি ভাষা। কোডটি প্রকাশ করার এবং উপস্থাপনের জন্য কার্যকরী ভাষাগুলির একটি নির্দিষ্ট উপায় রয়েছে। কিছু পরিস্থিতিতে এটি অ-কার্যকরী দৃষ্টান্তের চেয়ে উচ্চতর।
FP অনেক উচ্চতর হচ্ছে পরিচিত যখন এটি (তালিকা সাধিত আসে না করা তালিকা একটি জেনেরিক শব্দ, এর সম্পর্কহীন হিসাবে List<T>
)। এই সত্যটি দেওয়া, তালিকার বিষয়টি যখন আসে তখন আরও কার্যকরী উপায়ে সি # কোড প্রকাশ করার ক্ষমতাটি বরং একটি ভাল জিনিস।
যদি আপনি বিশ্বাসী না হন তবে বিষয়টিতে আমার পূর্ববর্তী উত্তরে ক্রিয়ামূলক এবং অ-কার্যকরী উভয় উপায়ে লিখিত কোডের পঠনযোগ্যতার তুলনা করুন ।