عملگرهای تبدیل Conversion Operator
عملگرهای پرس و جوی تبدیل، توالیهایی را که از جنس <IEnumerable<T هستند، به انواع دیگر مجموعه تبدیل میکنند.
از عملگرهای پرس و جوی زیر میتوان برای تبدیل توالیها استفاده کرد :
- OfType
- Cast
- ToArray
- ToList
- ToDictionary
-
ToLookup
عملگر OfType
این عملگر عناصری از توالی را که نوع آنها را مشخص میکنیم باز میگرداند.
امضاء عملگر پرس و جوی OfType به صورت زیر است :
public static IEnumerable<TResult> OfType<TResult>(this IEnumerable source)
همانطور که مشاهده میکنید توالی ورودی از یک نوع IEnumerable غیر جنریک میباشد. بدین معنی که عناصر توالی ورودی میتوانند از نوع دادههای مختلف باشند (توالی از اشیاء، از جنس Object).
در مثال زیر یک توالی IEnumerable (آرایهای از اشیاء)، از عناصر با نوع دادههای مختلفی را ایجاد کردهایم. عملگر OfType در اینجا کلیه عناصر از جنس (string) را باز میگرداند. توالی خروجی یک نوع IEnumerable جنریک است(در این مثال <IEnumerable<List).
مثال :
IEnumerable input = new object[] { "Apple", 33, "Sugar", 44, 'a', new DateTime()};
IEnumerable<string> query = input.OfType<string>();
foreach (var item in query)
{
Console.WriteLine(item);
}
خروجی مثال بالا :
عملگر OfType را میتوان بههمراه Strongly Typeها نیز استفاده کرد.
مثال :کد زیر یک ساختار سلسله مراتبی شیء گرا را نمایش میدهد:
class Ingredient
{
public string Name { get; set; }
}
class DryIngredient : Ingredient
{
public int Grams { get; set; }
}
class WetIngredient : Ingredient
{
public int Millilitres { get; set; }
}
کد زیر چگونگی استفاده از OfType را برای بدست آوردن یک زیر نوع (Subtype) مشخص، نشان میدهد (در این مثال، نوع WetIngredient):
IEnumerable<Ingredient> input = new Ingredient[]
{
new DryIngredient { Name = "Flour" },
new WetIngredient { Name = "Milk" },
new WetIngredient { Name = "Water" }
};
IEnumerable<WetIngredient> query = input.OfType<WetIngredient>();
foreach (WetIngredient item in query)
{
Console.WriteLine(item.Name);
}
خروجی مثال بالا :
پیاده سازی توسط عبارتهای جستجو
معادل این عملگر، کلمهی کلیدی جدیدی در عبارتهای جستجو وجود ندارد و ترکیب دو روش میتواند خروجی دلخواه را تولید کند.
عملگر Cast
عملگر Cast همانند عملگر OfType رفتار میکند. این عملگر یک توالی ورودی را دریافت و بر اساس نوع مشخص شده، توالی خروجی را تولید میکند. همهی عناصر توالی ورودی به نوع مشخص شده Cast میشوند. اما بر عکس عملگر OfType که عناصری را که با نوع دادهی ما سازگاری نداشت، نادیده میگرفت، این عملگر در صورت عدم موفقیت در عملیات تغییر نقش (Cast)، یک استثناء را پرتاب میکند.
مثال :
IEnumerable input = new object[]
{
"Apple", 33, "Sugar", 44, 'a', new DateTime()
};
IEnumerable<string> query = input.Cast<string>();
foreach (string item in query)
{
Console.WriteLine(item);
}
با اجرای برنامهی فوق، خطای زیر را مشاهده خواهید کرد:
Unhandled Exception: System.InvalidCastException: Unable to cast object of type 'System.Int32' to type 'System.String'.
پیاده سازی توسط عبارتهای جستجو
کلمهی کلیدی جایگزینی برای عملگر Cast، در عبارتهای جستجو وجود ندارد.این عملگر با استفاده از متغیر Range که در مطالب قبلی این
سریمعرفی شد، قابل پیاده سازی میباشد.
IEnumerable input = new object[]{ "Apple", "Sugar", "Flour" };
IEnumerable<string> query =
from string i in input
select i;
foreach (var item in query)
{
Console.WriteLine(item);
}
نکته: در مثال فوق تعریف صریح (Explicit) نوع داده، قبل از متغیر Range انجام شده است (معادل همان نوع داده در عملیات Cast).
عملگر ToArray
عملگر ToArray یک توالی ورودی را دریافت و یک توالی خروجی را به صورت آرایه تولید میکند. این عملگر باعث اجرای سریع پرس و جو میشود و رفتار پیش فرض LINQ را که اجرای با
تاخیرمیباشد، تحریف/بازنویسی (Override) میکند.
مثال: در این مثال یک توالی از نوع <IEnumerable<string به یک آرایه رشتهای تبدیل شده است (تبدیل لیست به آرایه).
IEnumerable<string> input = new List<string> { "Apple", "Sugar", "Flour" };
string[] array = input.ToArray();
پیاده سازی توسط عبارتهای جستجو
معادل این عملگر، کلمهی کلیدی جدیدی در عبارتهای جستجو وجود ندارد و ترکیب دو روش میتواند خروجی دلخواه را تولید کند.
عملگر ToList
عملگر ToList همچون ToArray، اجرای با تاخیر را نادیده میگیرد. عملگر ToList همانطور که از نامش پیداست، توالی خروجی را بهصورت لیست مهیا میکند.
مثال:
IEnumerable<string> input = new[] { "Apple", "Sugar", "Flour" };
List<string> list = input.ToList();
پیاده سازی توسط عبارتهای جستجو
معادل این عملگر، کلمهی کلیدی جدیدی در عبارتهای جستجو وجود ندارد و ترکیب دو روش میتواند خروجی دلخواه را تولید کند.
عملگر ToDictionary
این عملگر توالی ورودی را به یک دیکشنری جنریک تبدیل میکند (<Dictinary<TKey,TValue) .
سادهترین امضاء عملگر ToDictionary، یک عبارت Lambda میباشد. این عبارت Lambda نشان دهندهی یک تابع است که عنصر کلید(Key) را در دیکشنری، مشخص میکند.
مثال:
class Recipe
{
public int Id { get; set; }
public string Name { get; set; }
public int Rating { get; set; }
}
IEnumerable<Recipe> recipes = new[]
{
new Recipe { Id = 1, Name = "Apple Pie", Rating = 5 },
new Recipe { Id = 2, Name = "Cherry Pie", Rating = 2 },
new Recipe { Id = 3, Name = "Beef Pie", Rating = 3 }
};
Dictionary<int, Recipe> dict = recipes.ToDictionary(x => x.Id);
foreach (KeyValuePair<int, Recipe> item in dict)
{
Console.WriteLine($"Key={item.Key}, Recipe={item.Value}");
}
در کد بالا ،
کلیددیکشنری نهایی، از نوع int میباشد که بر اساس Id کلاس Recipe تنظیم شده است. مقادیر (value) دیکشنری هم همان اشیاء از جنس کلاس Recipe میباشند.
خروجی مثال بالا:
Key=1, Recipe=Apple Pie
Key=2, Recipe=Cherry Pie
Key=3, Recipe=Beef Pie
پیاده سازی توسط عبارتهای جستجو
معادل این عملگر، کلمهی کلیدی جدیدی در عبارتهای جستجو وجود ندارد و ترکیب دو روش میتواند خروجی دلخواه را تولید کند.
عملگر ToLookup
این عملگر رفتاری شبیه به عملگر ToDictionary را دارد، اما به جای تولید خروجی از نوع دیکشنری، نمونهای از جنس ILookUp را ایجاد میکند.
در کد زیر خروجی ایجاد شده توسط lookup دستورالعملها (Recipes) را بر حسب امتیاز آنها گروه بندی کرده است. در این مثال کلید، بر حسب Byte میباشد.
مثال :
class Recipe
{
public int Id { get; set; }
public string Name { get; set; }
public byte Rating { get; set; }
}
IEnumerable<Recipe> recipes = new[]
{
new Recipe { Id = 1, Name = "Apple Pie", Rating = 5 },
new Recipe { Id = 1, Name = "Banana Pie", Rating = 5 },
new Recipe { Id = 2, Name = "Cherry Pie", Rating = 2 },
new Recipe { Id = 3, Name = "Beef Pie", Rating = 3 }
};
ILookup<byte, Recipe> look = recipes.ToLookup(x => x.Rating);
foreach (IGrouping<byte, Recipe> ratingGroup in look)
{
byte rating = ratingGroup.Key;
Console.WriteLine($"Rating {rating}");
foreach (var recipe in ratingGroup)
{
Console.WriteLine($" - {recipe.Name}");
}
}
خروجی مثال بالا:
Rating 5
- Apple Pie
- Banana Pie
Rating 2
- Cherry Pie
Rating 3
- Beef Pie
پیاده سازی توسط عبارتهای جستجو
معادل این عملگر، کلمهی کلیدی جدیدی در عبارتهای جستجو وجود ندارد و ترکیب دو روش میتواند خروجی دلخواه را تولید کند.
عملگرهای عناصر Element Operators
این عملگرها، یک توالی ورودی را دریافت و تنها یک عنصر از توالی ورودی و یا یک عنصر را به عنوان عنصر پیش فرض باز میگردانند. این نوع عملگرها توالی خروجی را تولید نمیکنند.
عملگر First
این عملگر اولین عنصر توالی را باز میگرداند.
مثال :
Ingredient[] ingredients =
{
new Ingredient {Name = "Sugar", Calories = 500},
new Ingredient {Name = "Egg", Calories = 100},
new Ingredient {Name = "Milk", Calories = 150},
new Ingredient {Name = "Flour", Calories = 50},
new Ingredient {Name = "Butter", Calories = 500}
};
Ingredient element = ingredients.First();
Console.WriteLine(element.Name);
خروجی مثال بالا :
امضای دیگر این متد، امکان تعریف یک شرط را مهیا میکند. خروجی این حالت اولین عنصری است که شرط را تامین میکند. در کد زیر اولین عنصری که کالری آن برابر 150 باشد به خروجی ارسال میشود.
Ingredient[] ingredients =
{
new Ingredient {Name = "Sugar", Calories = 500},
new Ingredient {Name = "Egg", Calories = 100},
new Ingredient {Name = "Milk", Calories = 150},
new Ingredient {Name = "Flour", Calories = 50},
new Ingredient {Name = "Butter", Calories = 500}
};
Ingredient element = ingredients.First(x=>x.Calories==150);
Console.WriteLine(element.Name);
خروجی مثال بالا:
در زمان استفاده از عملگر First، اگر توالی ورودی هیچ عنصری نداشته باشد، یک استثناء رخ خواهد داد:
Unhandled Exception: System.InvalidOperationException: Sequence contains no elements
کد زیر نمونهای از این حالت است:
Ingredient[] ingredients = { };
Ingredient element = ingredients.First();
در زمان استفادهی از امضاء دیگر عملگر First، اگر هیچ عنصری شرط معرفی شدهی در پارامتر را تامین نکند، باز هم یک استثناء رخ خواهد داد:
Unhandled Exception: System.InvalidOperationException: Sequence contains no matching element
کد زیر حالت فوق را نشان میدهد:
Ingredient[] ingredients =
{
new Ingredient {Name = "Sugar", Calories = 500},
new Ingredient {Name = "Egg", Calories = 100},
new Ingredient {Name = "Milk", Calories = 150},
new Ingredient {Name = "Flour", Calories = 50},
new Ingredient {Name = "Butter", Calories = 500}
};
Ingredient element = ingredients.First(x=>x.Calories==1500);
پیاده سازی توسط عبارتهای جستجو
معادل این عملگر، کلمهی کلیدی جدیدی در عبارتهای جستجو وجود ندارد و ترکیب دو روش میتواند خروجی دلخواه را تولید کند.
عملگر FirstOrDefault
عملگر FirstOrDefalt همانند عملگر First عمل میکند، اما با این تفاوت که به جای پرتاب یک استثناء در شرایط معرفی شده در عملگر First، یک مقدار پیش فرض را بر اساس نوع عناصر توالی باز میگرداند. در صورتیکه توالی از نوع عددی باشد، مقدار 0 و اگر عناصر توالی از انواع ارجاعی باشند، مقدار Null و برای مقادیر منطقی، ارزش False بهعنوان مقادیر پیش فرض باز گردانده میشوند.
مثال :
Ingredient[] ingredients = { };
Ingredient element = ingredients.FirstOrDefault();
Console.WriteLine(element == null);
خروجی مثال بالا :
پیاده سازی حالتی که هیچ یک از عناصر با شرط عملگر کطالبقت ندارند.
Ingredient[] ingredients =
{
new Ingredient {Name = "Sugar", Calories = 500},
new Ingredient {Name = "Egg", Calories = 100},
new Ingredient {Name = "Milk", Calories = 150},
new Ingredient {Name = "Flour", Calories = 50},
new Ingredient {Name = "Butter", Calories = 500}
};
Ingredient element = ingredients.FirstOrDefault(x=>x.Calories==1500);
Console.WriteLine(element==null);
خروجی مثال بالا :
پیاده سازی توسط عبارتهای جستجو
معادل این عملگر، کلمهی کلیدی جدیدی در عبارتهای جستجو وجود ندارد و ترکیب دو روش میتواند خروجی دلخواه را تولید کند.
عملگر Last
این عملگر آخرین عنصر توالی را باز میگرداند. همچون عملگر First، این عملگر نیز یک امضاء برای دریافت یک عبارت شرط یا پیش بینی دارد. این پیش بینی، آخرین عنصری را که شرط را تامین کند، باز میگرداند. باز هم مثل عملگر First، در صورتی که توالی هیچ عنصری نداشته باشد و یا عدم تامین شرط توسط عناصر توالی، استثنایی رخ خواهد داد.
مثال :
Ingredient[] ingredients =
{
new Ingredient {Name = "Sugar", Calories = 500},
new Ingredient {Name = "Egg", Calories = 100},
new Ingredient {Name = "Milk", Calories = 150},
new Ingredient {Name = "Flour", Calories = 50},
new Ingredient {Name = "Butter", Calories = 500}
};
Ingredient element = ingredients.Last(x=>x.Calories==500);
Console.WriteLine(element.Name);
خروجی مثال بالا :
پیاده سازی توسط عبارتهای جستجو
معادل این عملگر، کلمهی کلیدی جدیدی در عبارتهای جستجو وجود ندارد و ترکیب دو روش میتواند خروجی دلخواه را تولید کند.
عملگر LastOrDefault
این عملگر همچون عملگر FirstOrDefault عمل میکند. از بروز استثناء جلوگیری کرده و مقدار پیش فرض را به خروجی ارسال میکند.
پیاده سازی توسط عبارتهای جستجو
معادل این عملگر، کلمهی کلیدی جدیدی در عبارتهای جستجو وجود ندارد و ترکیب دو روش میتواند خروجی دلخواه را تولید کند.
عملگر Single
عملگر
Single ، تنها عنصر توالی ورودی را باز میگرداند.در صورتی که توالی ما بیش از یک عنصر داشته باشد و یا توالی هیچ عنصری نداشته باشد، یک استثناء رخ خواهد داد.
Unhandled Exception: System.InvalidOperationException: Sequence contains more than one matching element
Unhandled Exception: System.InvalidOperationException: Sequence contains no matching element
مثال :
Ingredient[] ingredients =
{
new Ingredient { Name = "Sugar", Calories = 500 }
};
Ingredient element = ingredients.Single();
Console.WriteLine(element.Name);
خروجی مثال بالا :
عملگر Single، یک امضاء دیگر نیز دارد که یک عبارت پیش بینی را میپذیرد. در صورتی که بیش از یک عنصر، با پیش بینی مطابقت داشته باشد و یا هیچ عنصری شرط پیش بینی را تامین نکند، استثنائی رخ خواهد داد.
Ingredient[] ingredients =
{
new Ingredient { Name = "Sugar", Calories = 500 },
new Ingredient {Name = "Butter", Calories = 150},
new Ingredient {Name = "Milk", Calories = 500}
};
Ingredient element = ingredients.Single(x => x.Calories == 150);
Console.WriteLine(element.Name);
خروجی مثال بالا :
پیاده سازی توسط عبارتهای جستجو
معادل این عملگر، کلمهی کلیدی جدیدی در عبارتهای جستجو وجود ندارد و ترکیب دو روش میتواند خروجی دلخواه را تولید کند.
عملگر SingleOrDefault
عملگر SingleOrDefault همچون عملگر Single عمل میکند؛ اما با این تفاوت که اگر توالی هیچ عنصری نداشته باشد، مقدار پیش فرض نوع توالی، باز گردانده میشود و در صورتیکه هیچ عنصری شرط مشخص شده را تامین نکند، باز هم مقدار پیش فرض توالی، به جای رخ دادن استثناء باز گردانده میشود.
مثال : در این مثال هیچ عنصری با پیش بینی مشخص شده مطالبقت ندارد:
Ingredient[] ingredients =
{
new Ingredient { Name = "Sugar", Calories = 500 },
new Ingredient {Name = "Egg", Calories = 100},
new Ingredient {Name = "Milk", Calories = 50}
};
Ingredient element = ingredients.SingleOrDefault(x => x.Calories == 9999);
Console.WriteLine(element==null);
خروجی مثال بالا :
توجه داشته باشید که استثنائی رخ نداده است و مقدار پیش فرض انواع ارجاعی که Null میباشد باز گردانده شده است.
پیاده سازی توسط عبارتهای جستجو
معادل این عملگر، کلمهی کلیدی جدیدی در عبارتهای جستجو وجود ندارد و ترکیب دو روش میتواند خروجی دلخواه را تولید کند.
عملگر ElementAt
عملگر
ElementAt عنصری را در یک جایگاه مشخص شدهی در توالی، باز میگرداند.
مثال: در کد زیر سومین عنصر توالی ورودی انتخاب میشود:
Ingredient[] ingredients =
{
new Ingredient { Name = "Sugar", Calories = 500 },
new Ingredient {Name = "Egg", Calories = 100},
new Ingredient {Name = "Milk", Calories = 50}
};
Ingredient element = ingredients.ElementAt(2);
Console.WriteLine(element.Name);
خروجی مثال بالا :
باید دقت کرد که مقدار ارسالی به عملگر ElementAt، اندیسی با نقطهی آغاز صفر میباشد. بدین معنی که برای بدست آوردن اولین عنصر باید مقدار 0 را به عملگر ElementAt ارسال کرد. در صورتی که مقدار ارسالی با بازه اندیسهای عناصر توالی مطابقت نداشته باشد (بزرگتر از شماره اندیس آخرین عنصر توالی باشد) یک استثناء رخ خواهد داد.
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
پیاده سازی توسط عبارتهای جستجو
معادل این عملگر، کلمهی کلیدی جدیدی در عبارتهای جستجو وجود ندارد و ترکیب دو روش میتواند خروجی دلخواه را تولید کند.
عملگر ElementAtOrDefualt
عملگر ElementAtOrDefualt نیز همچون عملگر ElementAt کار میکند؛ اما در صورت وارد کردن اندیسی بزرگتر از اندیس مجاز توالی، دیگر یک استثناء رخ نخواهد داد و یک مقدار پیش فرض، بر اساس نوع عناصر توالی باز گردانده میشود.
مثال :
Ingredient[] ingredients =
{
new Ingredient { Name = "Sugar", Calories = 500 },
new Ingredient {Name = "Egg", Calories = 100},
new Ingredient {Name = "Milk", Calories = 50}
};
Ingredient element = ingredients.ElementAtOrDefault(5);
Console.WriteLine(element==null);
خروجی مثال بالا:
پیاده سازی توسط عبارتهای جستجو
معادل این عملگر، کلمهی کلیدی جدیدی در عبارتهای جستجو وجود ندارد و ترکیب دو روش میتواند خروجی دلخواه را تولید کند.
عملگر DefaultIfEmpty
عملگر DefaultIfEmpty یک توالی را دریافت کرده و به دو شکل عمل میکند:
1- اگر توالی شامل حداقل یک عنصر باشد، این توالی بدون هیچ تغییری به خروجی ارسال میشود.
2- اگر توالی هیچ عنصری نداشته باشد، توالی خروجی خالی نخواهد بود. در این حالت توالی خروجی تنها یک عضو دارد و آن هم مقدار پیش فرضی بر اساس نوع توالی میباشد.
مثال :
Ingredient[] ingredients =
{
new Ingredient { Name = "Sugar", Calories = 500 },
new Ingredient {Name = "Egg", Calories = 100},
new Ingredient {Name = "Milk", Calories = 50}
};
IEnumerable<Ingredient> query = ingredients.DefaultIfEmpty();
foreach (Ingredient item in query)
{
Console.WriteLine(item.Name);
}
خروجی مثال بالا :
همانطور که میبینید توالی خروجی دقیقا شبیه توالی ورودی میباشد.
کد زیر حالت دوم معرفی شدهی در تعریف DefaultIfEmpty را نشان میدهد.
Ingredient[] ingredients = { };
IEnumerable<Ingredient> query = ingredients.DefaultIfEmpty();
foreach (Ingredient item in query)
{
Console.WriteLine(item == null);
}
خروجی کد بالا :
پیاده سازی توسط عبارتهای جستجو
معادل این عملگر، کلمهی کلیدی جدیدی در عبارتهای جستجو وجود ندارد و ترکیب دو روش میتواند خروجی دلخواه را تولید کند.