۱۳۸۹/۰۵/۲۵

آموزش LINQ-قسمت دوم: خصوصیات C# برای LINQ


نکته خیلی مهم : این پست برای مطالعه توصیه نمی شود برای  یادگیری LINQ می توانید به " دوره آموزشی LINQ " مراجعه کنید.


در قسمت قبلی LINQ رو معرفی کردم و اینکه چگونه کار می کند ،همچنین به معرفی کلی چند Provider پرداختیم 
در این پست قصد دارم تا قابلیت های جدید C# 3 رو آموزش بدم ،به این دلیل که  برای یادگیری LINQ می بایست به آنها واقف شوید.

برای شروع از یک مثال ساده از LINQ شروع می کنیم

using System;
using System.Linq;
string[] myWords = { "hello world", "hello LINQ", "hello Aghdam" };
var items =
    from item in myWords
    where item.EndsWith("LINQ")
    select item;

foreach (var item in items)
    Console.WriteLine(item);

اگر کد بالا را اجرا کنید خروجی برابر با hello LINQ خواهد بود !

همانطور که مشاهده فرمودید عبارت کوئری بالا بسیار شبیه به گوئری های SQL است ،حالا می خواهیم قسمت های این کد را شرح دهیم.

   در قسمت گوئری یک متغیر از نوع var به نام items تعریف شده است که برای خروجی کوئری مورد استفاده قرار می گیرد ،فعلا با var کاری نداشته باشد . در ادامه این پست در مورد این کلمه کلیدی و کاربردهایش توضیح خواهم داد.
سپس items توسط یک عبارت پرس و جوی LINQ مقداردهی اولیه شده است. در قسمت اول از عبارت پرس و جو ،from برای تعیین نام منبع داده استفاده می شود . متغیر item در عبارت نشانگر یک عضو در مجموعه items است .
در قسمت where شرط های لازم برای بازیابی اطلاعات از منبع داده تبیین شده  است که تابع Endwith از کلاس string فراخوانی شده که در صورتی که قسمت پایانی رشته با "LINQ" به پایان برسد ،این تابع مقدار true برمی گرداند و سرانجام در قسمت select ،قسمت ها / بخش ها / یا فیلد های که می خواهیم نمایش دهیم را انتخاب می کنیم.

اگر متوجه شده باشید برای استفاده از LINQ می بایست از خصوصیات جدید C# استفاده کنیم ،برای اینکه بتوانیم درک بهتری از عبارات LINQ داشته باشیم لازم است تا این خصوصیات جدید را که در C# 2.0 و C# 3.0 به زبان C# اضافه گردیده را فرا بگیریم.
این خصوصیات جدید عبارتند از:
  • نوع بندی ضمنی - Type Inference
  • توابع الحاقی -  Extension Methods
  • عبارات لامبدا - Lambda Expressions
  • نوع های بي نام - Anonymous types
  • سازنده هاي پيشرفته - Object initializers
در اینجا کاربرد و قوانین استفاده از آنها را شرح می دهیم

نوع بندی ضمنی - Type Inference


    کلمه کلیدی var  (نوع بندی ضمنی)به کامپایلر اعلام می کند که خودش در مورد نوع متغیر تصمیم گیری کند و هیچ موقع برنامه نویس نمی تواند به صورت صریح نوع آن را مشخص کند.نمونه زیر یک مثال ساده از var را نشان می دهد

    var i=1;
    i="Hello LINQ"; // An error generated by this line
    

    در خط اول با مقدار دهی 1 به i کامپایلر نوع متغیر i را از نوع System.Int32 در نظر می گیرد ،با این اوصاف منطقی است که از خط دوم اشکال بگیرد.

    از این قابلیت می توان برای گاهش تکرار استفاده کرد مثلا کد زیر را در نظر بگیرید:

    List <int> myNumbers = new List<int>(1,2,3);
    

    کد بالا ما نیازی به قیدList<int> نداشتیم ،و می توانیم به صورت زیر بنویسیم:

    var myNumbers = new List<int>(1,2,3);
    

    توجه کنید که قابلیت کلمه کلیدی بیشتر از این حرفهاست و این مثال فقط برای آشنایی شما بود
    در استفاده ازمتغیر های که به صورت ضمنی نوع بندی شده اند ، محودیت های داریم که به معرفی آنها می پردازم

    اولین و مهم ترین محدودیت این است که نوع بندی ضمنی تنها به متغیر های درون یک تابع یا خصوصیت اعمال می شود.پس استفاده از کلمه کلیدی var برای تعریف مقادیر بازگشتی ،پارامترهاویا داده اختصاصی یک نوع غیر مجاز است:

    class varTestClass
    {
       //Error : var cannot be used as field Data!
       private var myNumber = 1;
       
       //Error : var cannot be used as return value 
       //or parameter type!
       public var myMethod(var x , var y)
       {
       }
    }
    

    همچنین متغیر های محلی تعریف شده با کلمه کلیدی var دقیقا باید با یک مقدار اولیه در زمان تعریف شده باشند و نم توانند با null رها شوند(همانند قوانین تعریف یک داده const).

    //error :must assign value!
    var myNumber;
    
    //error: must assign value at exact time of decleration!
    var myWord;
    myWord= "Hello LINQ";
    

    برای تکمیل مثال قبل توجه کنید که امکان تعریف یک متغیر محلی با نوع بندی ضمنی nullable با استفاده از توکن ? وجود ندارد.

    // can't define nullable implicit variable,
      //as implicit variables can never be initially assigned
      //null to begin with!
    
      var? myNumber = 1;
      var? noValue = null;
    
      Extension Methodes - توابع الحاقی
        توابع توسعه اجازه به دست آوردن کارایی را بدون نیاز به اصلاح مستقیم نوع مورد توسعه، به انواع کامپایل شده موجود و انواع در حال کامپایل کنونی می دهد. این تکنیک برای تزریق کارایی جدید به انواعی که کد پایه آنها وجود ندارد ،بسیار سودمند خواهد بود.
        در تعریف توابع الحاقی اولین محدودیتی که با آن روبه رو می شویم اینستکه آنها باید درون یک کلاس static تعریف شوند ،بنابراین هر تابع الحاقی می بایست با کلمه کلیدی static تعرف شود دومین محدودیت این است که ما برای اعلام این تابع به عنوان تابع الحاقی به کامپایلر می بایست با یک کلمه کلیدی this در اولین (و فقط اولین) پارامتر استفاده کنیم.
        برای مثال تابع توسعه ای که قبلا معرفی کرده ام را ببینید:

        ///  
         /// Returns a converted null and space to an empty string.  
         ///  
         public static string  ConvertNullToEmptyString(this string strInput )  
         {  
             return ( String.IsNullOrWhiteSpace(strInput)  ? string.Empty  :  strInput  );  
         }  
        

        این تابع به کلاس string  اضافه می شود و اگر درون رشته خالی باشد یا با space پر شده باشد ،مقدار خالی بر می گرداند در غیر اینصورت مقدار خورد رشته را برمی گرداند.
        نگاتی که باید در هنگام تعریف توابع توسعه باید به آنها توجه کنید:
        1. اگر یک متد  الحاقی تعریف کردید ولی یک متد داخلی با الگویی مشابه وجود داشت،اولویت فراخوانی با متد داخلی است.
        2. خصوصیات ،رویدادها و عملگرها قابل توسعه نیستند.

        عبارات لامبدا - Lambda Expressions
        عبارات Lambda ،عباراتی هستند که که یک تابع را عنوان خروجی برمی گردانند و توانایی تعریف توابع Inline رو به ما میدهند.توجه کنید که نوشتن عبارات Lambda هیچ پیچیدگی ندارد و خواهید فهمید که بسیار هم  LINQ و Delegate ها پر کاربرد هستند.
        برای تعریف یک تابع باید چهار قسمت را تعریف کنیم:
        1. نوع خروجی تابع
        2. نام تابع
        3. لیست پارامتر ها
        4. بدنه تابع
        توجه کنید برای تعریف لامبدا ما فقط دو مرحله از مراحل تعریف تابع را انجام می دهیم.
        1. لیست پارامتر ها
        2. بدنه تابع
        و در شرایطی خروجی تابع را نیز مشخص می کنیم

        بگذارید یک مثال بزنیم تا درک آن آسان تر شود:



        (int x)=> x+1;
        

        خوب کد بالا دقیقا معادل کر ذیر است:

        int func(int x)
        {
          return x+1;
        }
        
        در عبارت لامبدای بالا عبارت داخل پرانتز به عنوان آرگومان های تابع در نظر گرفته می شوند و اگر عبارت لامبدای مورد نظر شما فاقد آرگومان باشد می توانید از این قسمت صرف نظر کنید.
        قسمت دوم یعنی<= به کامپایلر اعلام می کند که این عبارت یک عبارت لامبدا است و قسمت سوم بدنه تابع را نشان می دهد که در این عبارت بدنه ساده است ولی اگر شما خواستید بدنه بیشتر را در عبارت خود داشته باشید می بایست دستورات درون brase قرار دهید و آنها را با سمی کولون جدا کنید.

        (int x)=>
        {
           x++;
           x*=2;
           return x;
        };
        

        Anonymous types – نوع های بي نام
        شما به عنوان یک برنامه نویس OO ، مزایای تعریف کلاس ها برای نمایش جزئیات و کارایی یک موجودیت برنامه نویسی را می دانید .هر وقت شما نیاز به تعریف یک کلاس داشته باشید ، آن را تعریف و پیاده سازی می کنید ولی هنگامی که شما می خواهید می خواهید کلاسی را برای مدلسازی مجموعه ای از داده ها ی کپسوله شده بدون تابع ،رویداد و یا کارایی سفارشی دیگری ایجاد کنید و حتی این مدل سازی فقط درون پروژه شما مورد استفاده قرار گرفته باشد و دیگر قصد استفاده از آن را نداشته باشید ،چکار انجام می دهید؟

        آیا کلاس جدیدی ایجاد می کنید ؟

        پاسخ من استفاده از Anonymous types است که یک میان بر بسیار بزرگ را در جلوی پای شما قرار می دهد .
        وقتی می خواهید یک نوع بی نام ایجاد کنید این کار را با استفاده از کلمه کلیدی var انجام می دهید.
        به مثال زیر توجه کنید.

        static void Main(string[] args)
        {
           var person = new
             {ID = 1 , FName = "Ali" , LName = "Aghdam" , Job = "Student" };
        
           Console.WriteLine("The Person  Name is {0} {1}.",person.FName ,person.LName);
           Console.ReadLine();
        }
        

        Object initializers – سازنده هاي پيشرفته

          امروزه در برنامه نویسی برای پیاده سازی موجودیت ها از کلاس ها استفاده می کنیم که در مهندسی نرم افزار به این روش Entity Typesاطلاق می شود که به عنوان بسته های اطلاعاتی محسوب می شوند ولی در طی این امر مشکلاتی وجود دارد که یکی از آن ها پیاده سازی سازنده های مختلف است.با قابلیت جدید سی شارپ یعنیObject initializer می توان تا حد بسیار زیادی از این پیچیدگی جلو گیری کرد و همچنین تا حد زیادی از بار کدنویسی کاست.
          به طور مثال موجودیت Person را با پیاده سازی زیر در نطر بگیرید که اطلا عاتی را پیاده سازی می کند.

          class Person
          {
            public int ID
            { get ; set ;}
          
            public string FName
            { get ; set ;}
          
            public string LName
            { get ; set ;}
          }
          

          خوب با توجه به موجودیت بالا که سه شناسه را تعریف کرده ،سازنده به چه شکلی خواهد بود ؟
          اگر از من بپرسید می گویم هیچ نیازی به استفاده از سازنده در مورد کلاس بالا نیست!  به چه شکل ،به شکل زیر:

          Person person = new Person 
            {
              ID =1 , 
              FName = "Ali" ,
              Lname = "Aghdam"
            };
          

          و حتی به صورت زیر :

          Person person = new Person 
            {
              ID =1 , 
              FName = "Ali"
            };
          

          با یادگیری ویژگی های جدید ،راه شما برای فراگیری LINQ بسیار هموار می گردد .

          در قسمت بعد در مورد عملگرهای استاندارد پرسو جو (Standard Query Operators) مطالبی را خدمتتون ارائه خواهم داد.

          Vote on iDevCenter

          ۴ نظر:

          1. عالی بود
            لطف می کنی اگر منابع ات را بگذاری

            پاسخحذف
          2. واقعا عالی بود.
            یه مشکل دارم نمی تونم مثل application تابعی که ورودی و خروجی داره تعریف کنم. خطا میگیره!

            پاسخحذف
          3. @ناشناس: حتما اشکال سینتکسی دارد، برای اطلاعات بیشتر به مقاله زیر مراجعه کنید:

            http://goo.gl/Odxfp
            و
            http://goo.gl/BCm7D

            پاسخحذف