مطالب
پیدا کردن آیتم‌های تکراری در یک لیست به کمک LINQ

گاهی از اوقات نیاز می‌شود تا در یک لیست، آیتم‌های تکراری موجود را مشخص کرد. به صورت پیش فرض متد Distinct برای حذف مقادیر تکراری در یک لیست با استفاده از LINQ موجود است که البته آن‌هم اما و اگرهایی دارد که در ادامه به آن پرداخته خواهد شد، اما باز هم این مورد پاسخ سؤال اصلی نیست (نمی‌خواهیم موارد تکراری را حذف کنیم).

برای حذف آیتم‌های تکراری از یک لیست جنریک می‌توان متد زیر را نوشت:
public static List<T> RemoveDuplicates<T>(List<T> items)
{
return (from s in items select s).Distinct().ToList();
}
برای مثال:
public static void TestRemoveDuplicates()
{
List<string> sampleList =
new List<string>() { "A1", "A2", "A3", "A1", "A2", "A3" };
sampleList = RemoveDuplicates(sampleList);
foreach (var item in sampleList)
Console.WriteLine(item);
}
این متد بر روی لیست‌هایی با نوع‌های اولیه مانند string‌ و int و امثال آن درست کار می‌کند. اما اکنون مثال زیر را در نظر بگیرید:
public class Employee
{
public int ID { get; set; }
public string FName { get; set; }
public int Age { get; set; }
}

public static void TestRemoveDuplicates()
{
List<Employee> lstEmp = new List<Employee>()
{
new Employee(){ ID=1, Age=20, FName="F1"},
new Employee(){ ID=2, Age=21, FName="F2"},
new Employee(){ ID=1, Age=20, FName="F1"},
};

lstEmp = RemoveDuplicates<Employee>(lstEmp);

foreach (var item in lstEmp)
Console.WriteLine(item.FName);
}
اگر متد TestRemoveDuplicates را اجرا نمائید، رکورد تکراری این لیست جنریک حذف نخواهد شد؛ زیرا متد distinct بکارگرفته شده نمی‌داند اشیایی از نوع کلاس سفارشی Employee را چگونه باید با هم مقایسه نماید تا بتواند موارد تکراری آن‌ها را حذف کند.
برای رفع این مشکل باید از آرگومان دوم متد distinct جهت معرفی وهله‌ای از کلاسی که اینترفیس IEqualityComparer را پیاده سازی می‌کند، کمک گرفت.
public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer);
که نمونه‌ای از پیاده سازی آن به شرح زیر می‌تواند باشد:

public class EmployeeComparer : IEqualityComparer<Employee>
{
public bool Equals(Employee x, Employee y)
{
//آیا دقیقا یک وهله هستند؟
if (Object.ReferenceEquals(x, y)) return true;

//آیا یکی از وهله‌ها نال است؟
if (Object.ReferenceEquals(x, null) ||
Object.ReferenceEquals(y, null))
return false;

return x.Age == y.Age && x.FName == y.FName && x.ID == y.ID;
}

public int GetHashCode(Employee obj)
{
if (Object.ReferenceEquals(obj, null)) return 0;
int hashTextual = obj.FName == null ? 0 : obj.FName.GetHashCode();
int hashDigital = obj.Age.GetHashCode();
return hashTextual ^ hashDigital;
}
}
اکنون اگر یک overload برای متد RemoveDuplicates با درنظر گرفتن IEqualityComparerتهیه کنیم، به شکل زیر خواهد بود:
public static List<T> RemoveDuplicates<T>(List<T> items, IEqualityComparer<T> comparer)
{
return (from s in items select s).Distinct(comparer).ToList();
}
به این صورت متد آزمایشی ما به شکل زیر (که وهله‌ای از کلاس EmployeeComparer‌ به آن ارسال شده) تغییر خواهد کرد:
public static void TestRemoveDuplicates()
{
List<Employee> lstEmp = new List<Employee>()
{
new Employee(){ ID=1, Age=20, FName="F1"},
new Employee(){ ID=2, Age=21, FName="F2"},
new Employee(){ ID=1, Age=20, FName="F1"},
};

lstEmp = RemoveDuplicates(lstEmp, new EmployeeComparer());

foreach (var item in lstEmp)
Console.WriteLine(item.FName);
}
پس از این تغییر، حاصل این متد تنها دو رکورد غیرتکراری می‌باشد.

سؤال: برای یافتن آیتم‌های تکراری یک لیست چه باید کرد؟
احتمالا مقاله "روش‌هایی برای حذف رکوردهای تکراری" را به خاطر دارید. اینجا هم می‌توان کوئری LINQ ایی را نوشت که رکوردها را بر اساس سن، گروه بندی کرده و سپس گروه‌هایی را که بیش از یک رکورد دارند، انتخاب نماید.
public static void FindDuplicates()
{
List<Employee> lstEmp = new List<Employee>()
{
new Employee(){ ID=1, Age=20, FName="F1"},
new Employee(){ ID=2, Age=21, FName="F2"},
new Employee(){ ID=1, Age=20, FName="F1"},
};

var query = from c in lstEmp
group c by c.Age into g
where g.Count() > 1
select new { Age = g.Key, Count = g.Count() };

foreach (var item in query)
{
Console.WriteLine("Age {0} has {1} records", item.Age, item.Count);
}
}


Vote on iDevCenter
مطالب
C# 7 - Generalized Async Return Types
از زمان ارائه‌ی C# 5 و معرفی الگوهای async/await، تنها نوع‌های خروجی پشتیبانی شده، <Task، Task<T و void (در موارد خاص) بودند. مشکل همراه با این روش، اجبار به وهله سازی رسمی یک Task است؛ حتی اگر نوع خروجی کاملا مشخص باشد.
برای نمونه در متد ذیل، میزان حجم مصرفی در یک پوشه بازگشت داده می‌شود:
public async Task<long> GetDirectorySize(string path, string searchPattern)
{
    if (!Directory.EnumerateFileSystemEntries(path, searchPattern).Any())
        return 0;
    else
        return await Task.Run<long>(() => Directory.GetFiles(path, searchPattern,
        SearchOption.AllDirectories).Sum(t => (new FileInfo(t).Length)));
}
اگر پوشه‌ای خالی باشد، حجم آن صفر است و در این حالت نیازی به ایجاد یک ترد مخصوص آن نیست. اما با توجه به اینکه خروجی متد، <Task<long است، هنوز هم باید این Task وهله سازی شود. برای نمونه اگر به کدهای IL آن دقت کنیم، return 0 آن به صورت ذیل ترجمه می‌شود:
 AsyncTaskMethodBuilder<long>.Create()

باید دقت داشت که Task، یک نوع ارجاعی است و استفاده‌ی از آن به معنای تخصیص حافظه‌است. اما زمانیکه قسمتی از کد کاملا همزمان اجرا می‌شود و یا مقداری کش شده را بازگشت می‌دهد، این تخصیص حافظه‌ی اضافی، خصوصا اگر در حلقه‌ها بکار گرفته شود، هزینه‌بر خواهد بود.


امکان تعریف خروجی‌های سفارشی متدهای async در C# 7.0

در C# 7 می‌توان خروجی‌های سفارشی را جهت متدهای async تعریف کرد و پیشنیاز اصلی آن پیاده سازی متد GetAwater است. برای مثال <System.Threading.Tasks.ValueTask<T یک چنین نوع سفارشی را ارائه می‌دهد. در این حالت، متد ابتدای بحث را می‌توان به صورت ذیل بازنویسی کرد:
public async ValueTask<long> GetDirectorySize(string path, string searchPattern)
{
    if (!Directory.EnumerateFileSystemEntries(path, searchPattern).Any())
        return 0;
    else
        return await Task.Run<long>(() => Directory.GetFiles(path, searchPattern,
        SearchOption.AllDirectories).Sum(t => (new FileInfo(t).Length)));
}
اگر دقت کنید بجز تغییر نوع خروجی متد، تغییر دیگری نیاز نبوده‌است.
همانطور که از نام  ValueTask نیز مشخص است، یک struct است؛ برخلاف Task و تخصیص حافظه‌ی آن بر روی stack بجای heap صورت می‌گیرد. به این ترتیب با کاهش فشار بر روی GC، در حلقه‌هایی که خروجی value type دارند، با اندازه گیری‌های انجام شده، کارآیی تا 50 درصد هم می‌تواند بهبود یابد.

برای کامپایل قطعه کد فوق و تامین نوع جدید ValueTask، نیاز به نصب بسته‌ی نیوگت ذیل نیز می‌باشد:
 PM> install-package System.Threading.Tasks.Extensions
بازخوردهای پروژه‌ها
استفاده از DataAnnotations , dynamic column
سلام 
یه سوال از حضورتون این کدی که در حال حاضر استفاده میکنم ، . میخوام از (DisplayName) استفاده کنم که باز هم از خود نام پروپرتی‌ها در codefirst  استفاده میکنه ، میشه بفرمایید کدوم قسمت رو باید تغییر بدم
public IPdfReportData CreatePdfReport(string title)
        {
            if (title=="" || title==null)
            {
                title = "گذارش";
            }
            return new PdfReport().DocumentPreferences(doc =>
               {
                   doc.RunDirection(PdfRunDirection.RightToLeft);
                   doc.Orientation(PageOrientation.Portrait);
                   doc.PageSize(PdfPageSize.A4);
                   doc.DocumentMetadata(new DocumentMetadata { Author = "Vahid", Application = "PdfRpt", Keywords = "Test", Subject = "Test Rpt", Title = "Test" });
               })
               .DefaultFonts(fonts =>
               {
                   fonts.Path(AppPath.ApplicationPath + "\\fonts\\BNazanin.ttf",
                                     Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\verdana.ttf");
               })
                .PagesFooter(footer =>
                {
                    footer.DefaultFooter(printDate: DateTime.Now.ToString("MM/dd/yyyy"));
                })
                .PagesHeader(header =>
                {
                    header.DefaultHeader(defaultHeader =>
                    {
                       // defaultHeader.ImagePath(AppPath.ApplicationPath + "\\Images\\01.png");
                        defaultHeader.Message(title);
                    });
                })
                .MainTableTemplate(template =>
                {
                    template.BasicTemplate(BasicTemplate.SilverTemplate);
                })
                .MainTablePreferences(table =>
                {
                    table.ColumnsWidthsType(TableColumnWidthType.Relative);
                })
                .MainTableDataSource(dataSource =>
                {
                    Cash.Models.ApplicationDbContext db = new Models.ApplicationDbContext();
                    var listOfRows = db.Users.Join(

                        db.CheckOuts, u => u.Id, c => c.ApplicationUserID,
                        (u, c) => new { u.FName, u.Lname, u.NationalCode, c.Creditor, c.Debtor, c.Decsription }
                        ).Where(x => x.Creditor != 0).ToList();


                    dataSource.AnonymousTypeList(listOfRows);
                })
                .MainTableSummarySettings(summary =>
                {
                    summary.OverallSummarySettings("جمع کل");
                    summary.PreviousPageSummarySettings("نقل از صفحه قبل");
                    summary.PageSummarySettings("جمع صفحه");
                })
                .MainTableAdHocColumnsConventions(adHocColumns =>
                {
                    //We want sum of the int columns
                    adHocColumns.AddTypeAggregateFunction(
                        typeof(Int64),
                        new AggregateProvider(AggregateFunction.Sum)
                        {
                            DisplayFormatFormula = obj => obj == null ? string.Empty : string.Format("{0:n0}", obj)
                        });
                    adHocColumns.AddTypeAggregateFunction(
                      typeof(Decimal),
                      new AggregateProvider(AggregateFunction.Sum)
                      {
                          DisplayFormatFormula = obj => obj == null ? string.Empty : string.Format("{0:n0}", obj)
                      });

                    //We want to dispaly all of the dateTimes as ShamsiDateTime
                    adHocColumns.AddTypeDisplayFormatFormula(
                        typeof(DateTime),
                        data => { return PersianDate.ToPersianDateTime((DateTime)data); }
                    );
                    adHocColumns.ShowRowNumberColumn(true);
                    adHocColumns.RowNumberColumnCaption("ردیف");
                })
                .MainTableEvents(events =>
                {
                    events.DataSourceIsEmpty(message: "هیچ اطلاعاتی موجود نیست");
                })
                .Export(export =>
                {
                    export.ToExcel();
                    export.ToXml();
                })
                .Generate(data => data.AsPdfFile(AppPath.ApplicationPath + "\\Pdf\\AdHocColumnsSampleRpt.pdf"));
        }

مطالب
برنامه نویسی پیشرفته JavaScript - قسمت 3 - انواع ارجاعی و نحوه‌ی ایجاد اشیاء

انواع ارجاعی

قبلا در مورد مقادیر ارجاعی صحبت کردیم. در اینجا نیز به این موضوع اشاره می‌کنیم که هر مقدار ارجاعی، نمونه‌ای ایجاد شده از یک نوع ارجاعی می‌باشد. انواع ارجاعی در واقع ساختارهایی هستند که جهت گروه بندی داده‌ها و عملکرد بین آنها استفاده می‌شوند. در سایر زبان‌های برنامه نویسی شیء گرا، به انواع ارجاعی، کلاس و به مقادیر ارجاعی، شیء می‌گویند. در جاوا اسکریپت نیز، به مقادیر ارجاعی و یا نمونه‌های ایجاد شده از انواع ارجاعی، شیء می‌گویند. به انواع ارجاعی، توصیف گر شیء نیز می‌گویند؛ زیرا ویژگی‌ها و متدهای آن شیء را معرفی و توصیف می‌نماید.


نحوه‌ی ایجاد شیء از نوع ارجاعی Object

از آنجاییکه نوع ارجاعی Object هیچ ویژگی و متد خاصی ندارد، متداول‌ترین نوع ارجاعی جهت ایجاد اشیاء سفارشی می‌باشد. به دو روش می‌توان نمونه‌ای را از یک Object ایجاد نمود. روش اول استفاده از عملگر new و بصورت زیر می‌باشد:

var person = new Object ();
person . name = "Meysam" ;
person . age = 32 ;

با استفاده از عملگر new، شیء person را از نوع Object ایجاد نمودیم که شامل دو ویژگی (Property) به نام‌های name و age می‌باشد. در واقع شیء person ساختاری را جهت نگهداری اطلاعات یک شخص معرفی می‌کند. این عمل موجب جلوگیری از پراکندگی تعریف متغیرها و گروه بندی آنها در قالب یک شیء می‌شود. روش دوم استفاده از Object Literal Notation یا نماد تحت الفظی شیء و بصورت زیر می‌باشد:

var person = {
    name : "Meysam" ,
    age : 32
};

Object Literal Notation ، دستور میانبری برای ایجاد یک شیء از نوع Object می‌باشد. در مثال فوق هم، همانند روش اول، شیء person را با دو ویژگی name و age ایجاد نمود‌ه‌ایم. در این روش، نام ویژگی‌ها می‌توانند به صورت رشته‌ای و عددی نیز به یک شیء اختصاص یابد.

var person = {
    "name" : "Meysam" ,
    "age" : 32
};

معمولا از دو روش فوق زمانی استفاده می‌شود که می‌خواهیم اشیایی را ایجاد نماییم که ویژگی‌های آن‌ها فقط خواندنی باشند. با استفاده از روش دوم، حتی می‌توان یک شیء خالی را ایجاد نمود که در ابتدا هیچ ویژگی ای ندارد  و در مراحل بعد، ویژگی‌هایی را به آن اضافه نمود.

var person = {}; // var person = new Object();
person . name = "Meysam" ;
person . age = 32 ;

در مثال فوق شیء person بدون ویژگی‌ها تعریف شده است؛ سپس به آن ویژگی‌هایی را اضافه نموده‌ایم.

استفاده از روش Object Literal Notation ، یکی از روش‌های محبوب برنامه نویسان جاوا اسکریپت می‌باشد. زیرا با کمترین کد و بصورت بصری، شیء ای را ایجاد نموده و مثلا به یک متد ارسال می‌نمایند. به مثال زیر توجه کنید:

function displayInfo ( arg ) {
    var output = "" ;
    if ( arg . name != undefined )
        output += "Name: " + arg . name + "\n" ;
    if ( arg . age != undefined )
        output += "Age: " + arg . age + "\n" ;
    return output ;
}

alert (displayInfo ({
    name : "Meysam" ,
    age : 32
}));

alert (displayInfo ({
    name : "Sohrab"
}));

در این مثال یک تابع تعریف شده است که آرگومان ورودی آن یک شیء می‌باشد. در تابع بررسی می‌شود که اگر ویژگی name و یا age برای این آرگومان تعریف شده بود، خروجی تابع را تولید نماید. در واقع این ویژگی‌ها اختیاری می‌باشند و می‌توانند ارسال نگردند. در زمان فراخوانی تابع نیز شیء ای را بصورت Object Literal Notation ایجاد نموده و به تابع ارسال کردیم.

این الگو برای ارسال آرگومان، زمانی استفاده می‌شود که تعداد زیادی آرگومان اختیاری داریم و می‌خواهیم به تابع ارسال نماییم. معمولا کار با آرگومان‌های نامی (Named Arguments) راحت‌تر است ولی زمانیکه تعداد آرگومان‌های اختیاری زیاد باشند، مدیریت و نگهداری آنها سخت و طاقت فرسا می‌گردد و ظاهر زشتی را به تابع می‌دهد. بهترین راهکار جهت مدیریت چنین شرایطی این است که آرگومان‌های اجباری را به صورت آرگومان‌های نامی تعریف کنیم و آرگومان‌های اختیاری را به صورت یک شیء به تابع ارسال کنیم.

نکته‌ی دیگری که باید به آن توجه نمود این است که جهت دسترسی به ویژگی‌های یک شیء از (.) استفاده می‌شود. همچنین می‌توان به یک ویژگی با استفاده از [] و بصورت یک آرایه دسترسی داشت که در این صورت نام ویژگی بصورت رشته‌ای در [] ذکر خواهد شد.

alert ( person . name );
alert ( person [ "name" ]);

در عمل تفاوتی بین دو مورد فوق وجود ندارد. مهمترین مزیت استفاده از [] این است که می‌توانید توسط یک متغیر به ویژگی‌های یک شیء دسترسی داشته باشید. همچنین اگر نام ویژگی شامل کاراکترهایی باشد که خطای گرامری رخ می‌دهد یا از اسامی رزرو شده استفاده کرده باشید، می‌توانید از [] جهت دسترسی به ویژگی استفاده نمایید.

var propertyName = "name" ;
alert ( person [ propertyName ]);
alert ( person [ "first name" ]);

در دستور alert اول، با استفاده از یک متغیر به ویژگی name از شیء person دسترسی پیدا نمودیم. در دستور آخر نیز، به دلیل وجود space در نام ویژگی، مجبور هستیم جهت دسترسی به ویژگی first name از [] استفاده نماییم.


بررسی نوع ارجاعی Function

توابع در واقع یک شیء و نمونه‌ای از نوع ارجاعی Function می‌باشند که می‌توانند همانند سایر اشیاء ویژگی‌ها و متدهای خاص خود را داشته باشند. بنابراین می‌توان در یک عبارت، تابعی را به یک شیء نسبت داد. به مثال زیر توجه کنید:

var sum = function ( a , b ) {
    return a + b ;
};

alert ( sum ( 10 , 20 ));

خروجی :

    30


شیء sum را تعریف نموده و یک تابع را به آن اختصاص دادیم که شامل دو آرگومان ورودی می‌باشد. حال می‌توان با شیء sum همانند یک تابع رفتار نمود و تابع مورد نظر را فراخوانی کرد. توجه داشته باشید که هیچ نامی را در زمان تعریف تابع به آن اختصاص نداده‌ایم. به این شکل تعریف تابع، Function Expression می‌گویند.

همانند سایر اشیاء، نام تابع نیز اشاره‌گری به تابع می‌باشد. بنابراین می‌توان توابع را نیز به یکدیگر نسبت داد. به مثال زیر توجه کنید:

function sum ( a , b ) {
    return a + b ;
}
alert ( sum ( 10 , 10 ));

var anotherSum = sum ;
alert ( anotherSum ( 10 , 10 ));

sum = null ;
alert ( anotherSum ( 10 , 10 ));
alert ( sum ( 10 , 10 )); // Error: Object is not a function;

خروجی :

    20

    20

    20

    Error: Object is not a function


ابتدا تابعی را به نام sum ایجاد نمودیم که دو عدد را با هم جمع می‌کند. دقت داشته باشید که به این شکل تعریف تابع sum ، اعلان تابع یا Function Declaration می‌گویند. سپس متغیری را به نام anotherSum ، تعریف نموده و sum را به آن نسبت دادیم. توجه داشته باشید که در زمان انتساب یک تابع به یک متغیر نباید () را ذکر کنیم، زیرا ذکر () به منزله‌ی فراخوانی تابع و اختصاص خروجی آن به متغیر می‌باشد و نه انتساب اشاره گر تابع به آن متغیر. فراخوانی sum و anotherSum خروجی یکسانی را دارند؛ زیرا هر دو به یک تابع اشاره می‌کنند. در خطوط بعدی، شیء sum را با مقدار null تنظیم نمودیم. در واقع با این کار اشاره‌گر sum برابر null شده است؛ یعنی دیگر به هیچ تابعی اشاره نمی‌کند. ولی تابع همچنان در حافظه وجود دارد؛ زیرا اشاره‌گر دیگری به نام anotherSum در حال اشاره نمودن به آن می‌باشد. در مرحله‌ی بعدی که sum را فراخوانی نمودیم، به دلیل null بودن آن، خطا رخ خواهد داد.


بازنگری مجدد به مبحث Overloading

در اینجا فقط می‌خواهیم اشاره‌ای کنیم به مبحث Overloading که قبلا در مورد آن بحث کردیم تا دلیل فنی عدم وجود Overloading را در جاوا اسکریپت درک کنیم. همانطور که قبلا بیان شد، نام تابع در واقع اشاره گری به تابع می‌باشد؛ بنابراین تعریف دو تابع هم نام، همانند اختصاص مجدد مقدار یا تغییر مقدار یک متغیر می‌باشد. به مثال زیر توجه کنید:

function calc ( num1 , num2 ) {
    return num1 + num2 ;
}

function calc ( num1 , num2 ) {
    return num1 - num2 ;
}

همانطور که پیشتر نیز عنوان شد، تابع دوم جایگزین تابع اول می‌گردد. در واقع تعریف فوق همانند تعریف زیر می‌باشد:

var calc = function ( num1 , num2 ) {
    return num1 + num2 ;
};

calc = function ( num1 , num2 ) {
    return num1 - num2 ;
};

همانطور که می‌بینید، ابتدا متغیری به نام calc تعریف شده‌است و با یک تابع مقداردهی اولیه شده است. سپس با تابع دوم مقداردهی مجدد گردیده است و دیگر به تابع قبلی اشاره نمی‌کند. بنابراین همیشه تابع آخر جایگزین توابع قبلی می‌گردد. 


Function Declaration در مقابل Function Expression

این دو روش تعریف و استفاده از توابع تقریبا مشابه هم می‌باشند و فقط یک تفاوت اصلی بین آنها وجود دارد و آن به نحوه‌ی رفتار موتور پردازشی جاوا اسکریپت بر می‌گردد. Function Declaration قبل از اینکه کدهای جاوا اسکریپت خوانده و اجرا شوند، خوانده شده و در دسترس خواهند بود؛ اما Function Expression تا زمانی که روال اجرای کد به این خط از کد نرسد، اجرا نخواهد شد و در دسترس نخواهد بود. به مثال Function Declaration زیر توجه کنید:

alert ( sum ( 10 , 20 ));

function sum ( a , b ) {
    return a + b ;
}

خروجی :

    30


قبل از اعلان تابع sum ، این تابع فراخوانی شده است؛ ولی هیچ خطایی رخ نمی‌دهد. زیرا قبل از اجرای دستورات، تابع sum خوانده و در دسترس خواهد بود. اما اگر تابع فوق بصورت Function Expression تعریف شده بود، خطا رخ می‌داد و برنامه اجرا نمی‌شد.

alert ( sum ( 10 , 20 )); // Error: undefined is not a function

var sum = function ( a , b ) {
    return a + b ;
};

خروجی :

    Error: undefined is not a function


همانطور که می‌بینید، در خط اول برنامه، خطای اجرایی رخ داده است. زیرا هنوز روال اجرایی برنامه به خط تعریف تابع sum نرسیده‌است. بنابراین تابع sum در دسترس نخواهد بود و فراخوانی آن موجب بروز خطا می‌گردد.


ارسال تابع به عنوان ورودی یا خروجی توابع دیگر

همانطور که قبلا بیان شد، نام تابع در واقع یک متغیر می‌باشد که به تابع مورد نظر اشاره می‌کند. بنابراین می‌توان همانند یک متغیر با آن رفتار نموده و به عنوان آرگومان ورودی و یا مقدار خروجی یک تابع آن را ارسال نمود. به مثال زیر توجه کنید:

function add ( a , b ) {
    return a + b ;
}

function mult ( a , b ) {
    return a * b ;
}

function calc ( a , b , fn ) {
    return a * b + fn ( a , b );
}

alert ( calc ( 10 , 20 , add ));
alert ( calc ( 10 , 20 , mult ));

خروجی :

    230

    400


دو تابع به نامهای add و mult با دو آرگومان ورودی تعریف شده‌اند که به ترتیب حاصل جمع و حاصل ضرب دو آرگومان را بر می‌گردانند. تابع calc نیز با 3 آرگومان ورودی تعریف شده‌است که قصد داریم برای آرگومان سوم یک تابع را ارسال نماییم. تابع calc در 2 مرحله فراخوانی شده‌است که در یک مرحله تابع add و در مرحله‌ی دیگر تابع mult به عنوان آرگومان ورودی ارسال شده‌اند. همانطور که از قبل می‌دانید، نام تابع اشاره‌گری به خود تابع می‌باشد. در تابع calc نیز با فراخوانی آرگومان fn در واقع داریم تابع ارسالی را فراخوانی می‌نماییم. حال به مثال زیر توجه کنید که یک تابع را به عنوان خروجی بر می‌گرداند:

function createFunction ( a , b ) {
    return function ( c ) {
        var d = ( a + b ) * c ;
        return d ;
    };
}

var fn = createFunction ( 10 , 20 );
alert ( fn ( 30 ));

خروجی :

    900


تابع createFunction دارای 2 آرگومان ورودی می‌باشد و یک تابع را با 1 آرگومان ورودی بر می‌گرداند. در ابتدا تابع createFunction را با آرگومان‌های 10 و 20 فراخوانی نمودیم. خروجی این تابع که خود یک تابع با یک آرگومان ورودی می‌باشد، به عنوان خروجی برگردانده شده و در متغیر fn قرار می‌گیرد. سپس تابع fn با آرگومان ورودی 30 فراخوانی می‌گردد که مقادیر 10 و 20 را با هم جمع نموده و در 30 ضرب می‌نماید. 

مطالب
ساختار داده‌های خطی Linear Data Structure قسمت دوم
در قسمت قبلی به مقدمات و ساخت لیست‌های ایستا و پویا به صورت دستی پرداختیم و در این قسمت (مبحث پایانی) لیست‌های آماده در دات نت را مورد بررسی قرار می‌دهیم.

کلاس ArrayList
این کلاس همان پیاده سازی لیست‌های ایستایی را دارد که در مطلب پیشین در مورد آن صحبت کردیم و نحوه کدنویسی آن نیز بیان شد و امکاناتی بیشتر از آنچه که در جدول مطلب پیشین گفته بودیم در دسترس ما قرار می‌دهد. از این کلاس با اسم untyped dynamically-extendable array به معنی آرایه پویا قابل توسعه بدون نوع هم اسم می‌برند چرا که به هیچ نوع داده‌ای مقید نیست و می‌توانید یکبار به آن رشته بدهید، یکبار عدد صحیح، یکبار اعشاری و یکبار زمان و تاریخ، کد زیر به خوبی نشان دهنده‌ی این موضوع است و نحوه استفاده‌ی از این آرایه‌ها را نشان می‌دهد.
using System;
using System.Collections;
 
class ProgrArrayListExample
{
    static void Main()
    {
        ArrayList list = new ArrayList();
        list.Add("Hello");
        list.Add(5);
        list.Add(3.14159);
        list.Add(DateTime.Now);
 
        for (int i = 0; i < list.Count; i++)
        {
            object value = list[i];
            Console.WriteLine("Index={0}; Value={1}", i, value);
        }
    }
}
نتیجه کد بالا:
Index=0; Value=Hello
Index=1; Value=5
Index=2; Value=3.14159
Index=3; Value=29.02.2015 23:17:01
البته برای خواندن و قرار دادن متغیرها از آنجا که فقط نوع Object را برمی‌گرداند، باید یک تبدیل هم انجام داد یا اینکه از کلمه‌ی کلیدی dynamic استفاده کنید:
ArrayList list = new ArrayList();
list.Add(2);
list.Add(3.5f);
list.Add(25u);
list.Add(" ریال");
dynamic sum = 0;
for (int i = 0; i < list.Count; i++)
{
    dynamic value = list[i];
    sum = sum + value;
}
Console.WriteLine("Sum = " + sum);
// Output: Sum = 30.5ریال

مجموعه‌های جنریک Generic Collections
مشکل ما در حین کار با کلاس arrayList و همه کلاس‌های مشتق شده از system.collection.IList این است که نوع داده‌ی ما تبدیل به Object می‌شود و موقعی‌که آن را به ما بر می‌گرداند باید آن را به صورت دستی تبدیل کرده یا از کلمه‌ی کلیدی dynamic استفاده کنیم. در نتیجه در یک شرایط خاص، هیچ تضمینی برای ما وجود نخواهد داشت که بتوانیم کنترلی بر روی نوع داده‌های خود داشته باشیم و به علاوه عمل تبدیل یا casting هم یک عمل زمان بر هست.
برای حل این مشکل، از جنریک‌ها استفاده می‌کنیم. جنریک‌ها می‌توانند با هر نوع داده‌ای کار کنند. در حین تعریف یک کلاس جنریک نوع آن را مشخص می‌کنیم و مقادیری که از آن به بعد خواهد پذیرفت، از نوعی هستند که ابتدا تعریف کرده‌ایم.
یک ساختار جنریک به صورت زیر تعریف می‌شود:
GenericType<T> instance = new GenericType<T>();
نام کلاس و به جای T نوع داده از قبیل int,bool,string را می‌نویسیم. مثال‌های زیر را ببینید:
List<int> intList = new List<int>();
List<bool> boolList = new List<bool>();
List<double> realNumbersList = new List<double>();

کلاس جنریک <List<T
این کلاس مشابه همان کلاس ArrayList است و فقط به صورت جنریک پیاده سازی شده است.
List<int> intList = new List<int>();
تعریف بالا سبب ایجاد ArrayList ـی می‌باشد که تنها مقادیر int را دریافت می‌کند و دیگر نوع Object ـی در کار نیست. یک آرایه از نوع int ایجاد می‌کند و مقدار خانه‌های پیش فرضی را نیز در ابتدا، برای آن در نظر می‌گیرد و با افزودن هر مقدار جدید می‌بیند که آیا خانه‌ی خالی وجود دارد یا خیر. اگر وجود داشته باشد مقدار جدید، به خانه‌ی بعدی آخرین خانه‌ی پر شده انتقال می‌یابد و اگر هم نباشد، مقدار خانه از آن چه هست 2 برابر می‌شود. درست است عملیات resizing یا افزایش طول آرایه عملی زمان بر محسوب میشود ولی همیشه این اتفاق نمی‌افتد و با زیاد شدن مقادیر خانه‌ها این عمل کمتر هم می‌شود. هر چند با زیاد شدن خانه‌ها حافظه مصرفی ممکن است به خاطر زیاد شدن خانه‌های خالی بدتر هم بشود. فرض کنید بار اول خانه‌ها 16 تایی باشند که بعد می‌شوند 32 تایی و بعدا 64 تایی. حالا فرض کنید به خاطر یک عنصر، خانه‌ها یا ظرفیت بشود 128 تایی در حالی که طول آرایه (خانه‌های پر شده) 65 تاست و حال این وضعیت را برای موارد بزرگتر پیش بینی کنید. در این نوع داده اگر منظور زمان باشد نتجه خوبی را در بر دارد ولی اگر مراعات حافظه را هم در نظر بگیرید و داده‌ها زیاد باشند، باید تا حدامکان به روش‌های دیگر هم فکر کنید.

چه موقع از <List<T استفاده کنیم؟
استفاده از این روش مزایا و معایبی دارد که باید در توضیحات بالا متوجه شده باشید ولی به طور خلاصه:
  • استفاده از index برای دسترسی به یک مقدار، صرف نظر از اینکه چه میزان داده‌ای در آن وجود دارد، بسیار سریع انجام میگیرد.
  • جست و جوی یک عنصر بر اساس مقدار: جست و جو خطی است در نتیجه اگر مقدار مورد نظر در آخرین خانه‌ها باشد بدترین وضعیت ممکن رخ می‌دهد و بسیار کند عمل می‌کند. داده هر چی کمتر بهتر و هر چه بیشتر بدتر. البته اگر بخواهید مجموعه‌ای از مقدارهای برابر را برگردانید هم در بدترین وضعیت ممکن خواهد بود.
  • حذف و درج (منظور insert) المان‌ها به خصوص موقعی که انتهای آرایه نباشید، شیفت پیدا کردن در آرایه عملی کاملا کند و زمانبر است.
  • موقعی که عنصری را بخواهید اضافه کنید اگر ظرفیت آرایه تکمیل شده باشد، نیاز به عمل زمانبر افزایش ظرفیت خواهد بود که البته این عمل به ندرت رخ می‌دهد و عملیات افزودن Add هم هیچ وابستگی به تعداد المان‌ها ندارد و عملی سریع است.
با توجه به موارد خلاصه شده بالا، موقعی از لیست اضافه می‌کنیم که عملیات درج و حذف زیادی نداریم و بیشتر برای افزودن مقدار به انتها و دسترسی به المان‌ها بر اساس اندیس باشد.

<LinkedList<T
یک کلاس از پیش آماده در دات نت که لیست‌های پیوندی دو طرفه را پیاده سازی می‌کند. هر المان یا گره یک متغیر جهت ذخیره مقدار دارد و یک اشاره گر به گره قبل و بعد.
چه موقع باید از این ساختار استفاده کنیم؟
از مزایا و معایب آن :
  • افزودن به انتهای لیست به خاطر این که همیشه گره آخر در tail وجود دارد بسیار سریع است.
  • عملیات درج insert در هر موقعیتی که باشد اگر یک اشاره گر به آن محل باشد یک عملیات سریع است یا اینکه درج در ابتدا یاانتهای لیست باشد.
  • جست و جوی یک مقدار چه بر اساس اندیس باشد و چه مقدار، کار جست و جو کند خواهد بود. چرا که باید تمامی المان‌ها از اول به آخر اسکن بشن.
  • عملیات حذف هم به خاطر اینکه یک عمل جست و جو در ابتدای خود دارد، یک عمل کند است.
استفاده از این کلاس موقعی خوب است که عملیات‌های درج و حذف ما در یکی از دو طرف لیست باشد یا اشاره‌گری به گره مورد نظر وجود داشته باشد. از لحاظ مصرف حافظه به خاطر داشتن فیلدهای اشاره‌گر به جز مقدار، زیاد‌تر از نوع List می‌باشد. در صورتی که دسترسی سریع به داده‌ها برایتان مهم باشد استفاده از List باز هم به صرفه‌تر است.

پشته Stack
یک سری مکعب را تصور کنید که روی هم قرار گرفته اند و برای اینکه به یکی از مکعب‌های پایینی بخواهید دسترسی داشته باشید باید تعدادی از مکعب‌ها را از بالا بردارید تا به آن برسید. یعنی بر خلاف موقعی که آن‌ها روی هم می‌گذاشتید و آخرین مکعب روی همه قرار گرفته است. حالا همان مکعب‌ها به صورت مخالف و معکوس باید برداشته شوند.
یک مثال واقعی‌تر و ملموس‌تر، یک کمد لباس را تصور کنید که مجبورید برای آن که به لباس خاصی برسید، باید آخرین لباس‌هایی را که در داخل کمد قرار داده‌اید را اول از همه از کمد در بیاورید تا به آن لباس برسید.
در واقع  پشته چنین ساختاری را پیاده می‌کند که اولین عنصری که از پشته بیرون می‌آید، آخرین عنصری است که از آن درج شده است و به آن LIFO گویند که مخفف عبارت Last Input First Output آخرین ورودی اولین خروجی است. این ساختار از قدیمی‌ترین ساختارهای موجود است. حتی این ساختار در سیستم‌های داخل دات نت CLR هم به عنوان نگهدارنده متغیرها و پارامتر متدها استفاده می‌شود که به آن Program Execution Stack می‌گویند.
پشته سه عملیات اصلی را پیاده سازی می‌کند: Push جهت قرار دادن مقدار جدید در پشته، POP جهت بیرون کشیدن مقداری که آخرین بار در پشته اضافه شده و Peek جهت برگرداندن آخرین مقدار اضافه شده به پشته ولی آن مقدار از پشته حذف نمی‌شود.
این ساختار میتواند پیاده سازی‌های متفاوتی را داشته باشد ولی دو نوع اصلی که ما بررسی می‌کنیم، ایستا و پویا بودن آن است. ایستا بر اساس آرایه است و پویا بر اساس لیست‌های پیوندی. شکل زیر پشته‌ای را به صورت استفاده از پیاده‌سازی ایستا با آرایه‌ها نشان می‌دهد و کلمه Top به بالای پشته یعنی آخرین عنصر اضافه شده اشاره می‌کند.

استفاده از لیست پیوندی برای پیاده سازی پشته:

لیست پیوندی لازم نیست دو طرفه باشد و یک طرف برای کار با پشته مناسب است و دیگر لازم نیست که به انتهای لیست پیوندی عمل درج انجام شود؛ بلکه مقدار جدید به ابتدای آن اضافه شده و برای حذف گره هم اولین گره باید حذف شود و گره دوم به عنوان head شناخته می‌شود. همچنین لیست پیوندی نیازی به افزایش ظرفیت مانند آرایه‌ها ندارد.
ساختار پشته در دات نت توسط کلاس Stack از قبل آماده است:
Stack<string> stack = new Stack<string>();
stack.Push("A");
stack.Push("B");
stack.Push("C");
 while (stack.Count > 0)
    {
        string letter= stack.Pop();
        Console.WriteLine(letter);
    }
//خروجی
//C
//B
//A

صف Queue
ساختار صف هم از قدیمی‌ترین ساختارهاست و مثال آن در همه جا و در همه اطراف ما دیده می‌شود؛ مثل صف نانوایی، صف چاپ پرینتر، دسترسی به منابع مشترک توسط سیستمها. در این ساختار ما عنصر جدید را به انتهای صف اضافه می‌کنیم و برای دریافت مقدار، عنصر را از ابتدا حذف می‌کنیم. به این ساختار FIFO مخفف First Input First Output به معنی اولین ورودی و اولین خروجی هم می‌گویند.
ساختار ایستا که توسط آرایه‌ها پیاده سازی شده است:

ابتدای آرایه مکانی است که عنصر از آنجا برداشته می‌شود و Head به آن اشاره می‌کند و tail هم به انتهای آرایه که جهت درج عنصر جدید مفید است. با برداشتن هر خانه‌ای که head به آن اشاره می‌کند، head یک خانه به سمت جلو حرکت می‌کند و زمانی که Head از tail بیشتر شود، یعنی اینکه دیگر عنصری یا المانی در صف وجود ندارد و head و Tail به ابتدای صف حرکت می‌کنند. در این حالت موقعی که المان جدیدی قصد اضافه شدن داشته باشد، افزودن، مجددا از اول صف آغاز می‌شود و به این صف‌ها، صف حلقوی می‌گویند.

عملیات اصلی صف دو مورد هستند enqueue که المان جدید را در انتهای صف قرار می‌دهد و dequeue اولین المان صف را بیرون می‌کشد.


پیاده سازی صف به صورت پویا با لیست‌های پیوندی

برای پیاده سازی صف، لیست‌های پیوندی یک طرفه کافی هستند:

در این حالت عنصر جدید مثل سابق به انتهای لیست اضافه می‌شود و برای حذف هم که از اول لیست کمک می‌گیریم و با حذف عنصر اول، متغیر Head به عنصر یا المان دوم اشاره خواهد کرد.

کلاس از پیش آمده صف در دات نت <Queue<T است و نحوه‌ی استفاده آن بدین شکل است:

static void Main()
{
    Queue<string> queue = new Queue<string>();
    queue.Enqueue("Message One");
    queue.Enqueue("Message Two");
    queue.Enqueue("Message Three");
    queue.Enqueue("Message Four");
 
    while (queue.Count > 0)
    {
        string msg = queue.Dequeue();
        Console.WriteLine(msg);
    }
}
//خروجی
//Message One
//Message Two
//Message Thre
//Message Four


مطالب
تهیه خروجی XML از یک بانک اطلاعاتی، توسط EF Code first
نگارش کامل SQL Server امکان تهیه خروجی XML از یک بانک اطلاعاتی را دارد. اما اگر بخواهیم از سایر بانک‌های اطلاعاتی که چنین توابع توکاری ندارند، استفاده کنیم چطور؟ برای تهیه خروجی XML توسط Entity framework و مستقل از نوع بانک اطلاعاتی در حال استفاده، حداقل دو روش وجود دارد:

الف) استفاده از امکانات Serialization توکار دات نت

using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace DNTViewer.Common.Toolkit
{
    public static class Serializer
    {
        public static string Serialize<T>(T type)
        {
            var serializer = new XmlSerializer(type.GetType());
            using (var stream = new MemoryStream())
            {
                serializer.Serialize(stream, type);
                stream.Seek(0, SeekOrigin.Begin);
                using (var reader = new StreamReader(stream))
                {
                    return reader.ReadToEnd();
                }
            }
        }
    }
}
در اینجا برای نمونه، لیستی از اشیاء مدنظر خود را تهیه کرده و به متد Serialize فوق ارسال کنید. نتیجه کار، تهیه معادل XML آن است.
امکانات سفارشی سازی محدودی نیز برای XmlSerializer درنظر گرفته شده است؛ برای نمونه قرار دادن ویژگی‌هایی مانند XmlIgnore بالای خواصی که نیازی به حضور آن‌ها در خروجی نهایی XML نمی‌باشد.


ب) استفاده از امکانات LINQ to XML دات نت

روش فوق بدون مشکل کار می‌کند، اما اگر بخواهیم قسمت Reflection خودکار ثانویه آن‌را (برای نمونه جهت استخراج مقادیر از لیست دریافتی) حذف کنیم، می‌توان از LINQ to XML استفاده کرد که قابلیت سفارشی سازی بیشتری را نیز در اختیار ما قرار می‌دهد (کاری که در سایت جاری برای تهیه خروجی XML از بانک اطلاعاتی آن انجام می‌شود).

        private string createXmlFile(string dir)
        {
            var xLinq = new XElement("ArrayOfPost",
                        _blogPosts
                            .AsNoTracking()
                            .Include(x => x.Comments)
                            .Include(x => x.User)
                            .Include(x => x.Tags)
                            .OrderBy(x => x.Id)
                            .ToList()
                            .Select(x => new XElement("Post", postXElement(x)))
                            );

            var xmlFile = Path.Combine(dir, "dot-net-tips-database.xml");
            xLinq.Save(xmlFile);
            return xmlFile;
        }

        private static XElement[] postXElement(BlogPost x)
        {
            return new XElement[]
            {
                new XElement("Id", x.Id),
                new XElement("Title", x.Title),
                new XElement("Body", x.Body),
                new XElement("CreatedOn", x.CreatedOn),
                tagElement(x),
                new XElement("User",
                                new XElement("Id", x.UserId.Value),
                                new XElement("FriendlyName", x.User.FriendlyName))
            }.Where(item => item != null).ToArray();
        }

        private static XElement tagElement(BlogPost x)
        {
            var tags = x.Tags.Any() ?
                            x.Tags.Select(y =>
                                        new XElement("Tag",
                                                new XElement("Id", y.Id),
                                                new XElement("Name", y.Name)))
                                  .ToArray() : null;
            if (tags == null)
                return null;

            return new XElement("Tags", tags);
        }
خلاصه‌ای از نحوه تبدیل اطلاعات لیستی از مطالب را به معادل XML آن در کدهای فوق مشاهده می‌کنید. یک سری نکات ریز نیز باید در اینجا رعایت شوند:
1) کار با یک new XElement که دارای متد Save با فرمت XML نیز هست، شروع می‌شود. مقدار آن‌را مساوی یک کوئری از بانک اطلاعاتی قرار می‌دهیم. این کوئری چون قرار است تنها اطلاعاتی را از بانک اطلاعاتی دریافت کند و نیازی به تغییر در آن‌ها نیست، با استفاده از متد AsNoTracking، حالت فقط خواندنی پیدا کرده است.
2) اطلاعاتی را که نیاز است در فایل نهایی XML وجود داشته باشند، تنها کافی است در قسمت Select این کوئری با فرمت new XElement‌های تو در تو قرار دهیم. به این ترتیب قسمت Relection خودکار XmlSerializer روش مطرح شده در ابتدای بحث دیگر وجود نداشته و عملیات نهایی بسیار سریعتر خواهد بود.
3) چون در این حالت، کار انجام شده دستی است، باید نام‌های گره‌های صحیحی را انتخاب کنیم تا اگر قرار است توسط همان XmlSerializer مجددا کار serializer.Deserialize صورت گیرد، عملیات با شکست مواجه نشود. بهترین کار برای کم شدن سعی و خطاها، تهیه یک لیست اطلاعات آزمایشی و سپس ارسال آن به روش ابتدای بحث است. سپس می‌توان با بررسی خروجی آن مثلا دریافت که روش serializer.Deserialize به صورت پیش فرض به دنبال ریشه‌ای به نام ArrayOfPost برای دریافت لیستی از مطالب می‌گردد و نه Posts یا هر نام دیگری.
4) در کوئری LINQ to Entites نوشته شده، پیش از Select، یک ToList قرار دارد. متاسفانه EF اجازه استفاده مستقیم از Select هایی از نوع XElement را نمی‌دهد و باید ابتدا اطلاعات را تبدیل به LINQ to Objects کرد.
5) در حین تهیه XElement‌ها اگر قرار است عنصری نال باشد، باید آن‌را در خروجی نهایی ذکر نکرد. به این ترتیب serializer.Deserialize بدون نیاز به تنظیمات اضافه‌تری بدون مشکل کار خواهد کرد. در غیراینصورت باید وارد مباحثی مانند تعریف یک فضای نام جدید برای خروجی XML به نام XSI رفت و سپس به کمک ویژگی‌ها، xsi:nil را به true مقدار دهی کرد. اما همانطور که در متد postXElement ملاحظه می‌کنید، برای وارد نشدن به مبحث فضای نام xsi، مواردی که null بوده‌اند، اصلا در آرایه نهایی ظاهر نمی‌شوند و نهایتا در خروجی، حضور نخواهند داشت. به این ترتیب متد ذیل، بدون مشکل و بدون نیاز به تنظیمات اضافه‌تری قادر است فایل XML نهایی را تبدیل به معادل اشیاء دات نتی آن کند.

using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace DNTViewer.Common.Toolkit
{
    public static class Serializer
    {
        public static T DeserializePath<T>(string xmlAddress)
        {
            using (var xmlReader = new XmlTextReader(xmlAddress))
                {
                    var serializer = new XmlSerializer(typeof(T));
                    return (T)serializer.Deserialize(xmlReader);
                }
        }
    }
}
مطالب
refactoring کدهای تکراری به کمک Action‌ها

در برخی از مواقع بر روی اشیاء یک لیست، در یک کلاس، با استفاده از حلقه‌های foreach یا for کارهای متفاوتی انجام می‌شود. به عنوان مثال در یک لیست که از سطرهای فاکتور تشکیل شده است، می‌خواهیم جمع مقادیر کلیه سطرهای فاکتور یا جمع مبلغ یا مالیات یا تخفیف آنها را بدست آوریم . با وجود سادگی حلقه‌های foreach و for، ممکن است که در برخی از مواقع از راه متفاوتی استفاده شود. برای مثال اجازه بدهید مثال ذیل را با هم بررسی کنیم:
در کلاس Invoice دو متد وجود دارد با نام های  CalculateTotalTax  و CalculateTotal
متد CalculateTotalTax مجموع مالیات و متد CalculateTotal مجموع مقدار این فاکتور را بدست می‌آورد .

        public float CalculateTotalTax1()
        {
            IList<InvoiceLineItem> invoiceLineItem = new List<InvoiceLineItem>();
            Decimal result = 0M;
            foreach (InvoiceLineItem index in invoiceLineItem)
            {
                result += (Decimal)index.CalculateTax();
            }
            return (float)result;
        }


        public float CalculateTotal()
        {
            IList<InvoiceLineItem> invoiceLineItem = new List<InvoiceLineItem>();
            Decimal result = 0M;
            foreach (InvoiceLineItem index in invoiceLineItem)
            {
                result += (Decimal)index.CalculateSubTotal();
            }
            return (float)result;
        }
این دو متد به طور عمومی یک چیز را دارند: هر دو کارهای متفاوتی را بر روی لیست سطرهای فاکتور انجام می‌دهند.

ما می‌توانیم مسئولیت چرخش در لیست سطرهای فاکتور را از این متدها برداریم و آن را از IEnumerable جدا کنیم؛ به وسیله ایجاد یک متد که پارامتر ورودی  Action<T> delegate را دریافت می‌کند و این delegate را برای هر سطر در هر چرخش اجرا می‌کند.

        public void PerformActionOnAllLineItems(Action<InvoiceLineItem> action)
        {
            IList<InvoiceLineItem> invoiceLineItem = new List<InvoiceLineItem>();

            invoiceLineItem.Add(new InvoiceLineItem { Id = 1, amount = 10, Price = 10000 });
            invoiceLineItem.Add(new InvoiceLineItem { Id = 2, amount = 10, Price = 10000 });
            invoiceLineItem.Add(new InvoiceLineItem { Id = 3, amount = 10, Price = 10000 });
            invoiceLineItem.Add(new InvoiceLineItem { Id = 4, amount = 10, Price = 10000 });

            foreach (InvoiceLineItem index in invoiceLineItem)
            {
                action(index);
            }
        }

و همچنین می‌توانیم دو متد خود را به شکل ذیل تغییر دهیم

        float CalculateTotal()
        {
            Decimal result = 0M;
            PerformActionOnAllLineItems(delegate(InvoiceLineItem ili)
            {
                result += (Decimal)ili.CalculateSubTotal();
            });
            return (float)result;
        }
        float CalculateTotalTax()
        {
            Decimal result = 0M;
            PerformActionOnAllLineItems(delegate(InvoiceLineItem ili)
            {
                result += (Decimal)ili.CalculateTax();
            });
            return (float)result;
        }
 منبع : کتاب  Refactoring with Microsoft Visual Studio 2010
سورس نمونه :Advancedduplicatecoderefactoring.rar  
نظرات مطالب
به دست آوردن اطلاعات کد اجراکننده یک متد
اضافه شدن ویژگی CallerArgumentExpression به C#10.0

ویژگی [CallerArgumentExpression] امکان دریافت آرگومان ارسالی به یک متد را به صورت رشته‌ای میسر می‌کند. برای مثال:
public static void LogExpression<T>(
    T value, 
    [CallerArgumentExpression("value")] string expression = null)
{
    Console.WriteLine($"{expression}: {value}");
}
با ورودی زیر:
var person = new Person("Vahid", "N.");
LogExpression(person.FirstName);
چنین خروجی را تولید می‌کند:
person.FirstName: Vahid

پارامتری که توسط ویژگی [CallerArgumentExpression] معرفی می‌شود، اختیاری بوده و به صورت خودکار توسط کامپایلر مقدار دهی می‌شود. یعنی کامپایلر فراخوانی فوق را به صورت زیر انجام می‌دهد:
LogExpression(person.FirstName, "person.FirstName");
وجود یک چنین قابلیتی، نویسندگان کتابخانه‌ها را از بکارگیری <<Expression<Func<T‌ها یا همان استاتیک ریفلکشن، رهایی می‌بخشد.

یک نمونه کاربرد دیگر آن در بررسی نال بودن مقدار پارامترهای ارسالی است که می‌توان آن‌ها را به صورت زیر خلاصه کرد:
public static void EnsureArgumentIsNotNull<T>(
   T value, 
   [CallerArgumentExpression("value")] string expression = null)
{
    if (value is null)
        throw new ArgumentNullException(expression);
}

public static void Foo(string name)
{
    EnsureArgumentIsNotNull(name); // if name is null, throws ArgumentNullException: "Value cannot be null. (Parameter 'name')"
    ...
}
پیشتر می‌بایستی با استفاده از nameof، نام پارامتر را مشخص کرد. در اینجا کامپایلر قادر است این مقدار را مشخص کند و دیگر نیازی به استفاده از روش زیر نیست:
    if (name is null)
    {
        throw new ArgumentNullException(nameof(name));
    }
البته NET 6.0. به همراه متد جدید زیر که از قابلیت فوق استفاده می‌کند، هست:
ArgumentNullException.ThrowIfNull(name);
و متد ThrowIfNull آن به صورت زیر تعریف شده‌است:
public static void ThrowIfNull(
    [NotNull] object? argument,
    [CallerArgumentExpression("argument")] string? paramName = null)

سؤال: چرا آرگومان اول این متد، هم nullable تعریف شده‌است و هم با ویژگی NotNull مزین گشته‌است؟
nullable بودن آن از این جهت است که ممکن است مقدار ارسالی به آن نال باشد. ویژگی NotNull آن به کامپایلر اعلام می‌کند که اگر این متد با موفقیت به پایان رسید، در سطرهای پس از آن، مقدار این شیء دیگر نال نیست و نیازی نیست تا به استفاده کنند اعلام کند که باید مراقب نال بودن آن باشد.
مطالب
Value Types ارجاعی در C# 7.2
در C# 7.2 می‌توان با value types (مانند structs) همانند reference types (مانند کلاس‌ها) رفتار کرد. جائیکه کارآیی برنامه بسیار حائز اهمیت باشد (مانند بازی‌ها)، استفاده از structs و value types بسیار مرسوم است؛ از این جهت که این نوع‌ها بر روی heap تخصیص داده نمی‌شوند. اما مشکل آن‌ها این است که زمانیکه به متدها ارسال می‌شوند، مقدار آن‌ها ارسال خواهد شد و برای این منظور نیاز به ایجاد یک کپی جدید از آن‌ها می‌باشد. برای رفع این مشکل و کاهش سربار کپی کردن اشیاء، اکنون در C# 7.2 می‌توان value types را همانند reference types به متدها ارسال کرد.


واژه‌ی کلیدی جدید in

C# 7.2‌، واژه‌ی کلیدی جدیدی را به نام in جهت تعریف پارامترها، معرفی کرده‌است. زمانیکه از آن استفاده می‌شود به این معنا است که value type ارسالی به آن، توسط ارجاعی از آن، در اختیار متد قرار می‌گیرد و نه توسط مقدار کپی شده‌ی آن (حالت پیش‌فرض) و همچنین متد استفاده کننده‌ی از آن، مقدار این شیء را تغییر نمی‌دهد.
واژه‌ی کلیدی in مکمل واژه‌های کلیدی ref و out است که پیشتر به همراه زبان #C ارائه شده بودند:
- واژه‌ی کلیدی out: مقدار آرگومان مزین شده‌ی توسط آن، باید درون متد تنظیم شود و صرفا کاربرد ارائه‌ی یک خروجی اضافه‌تر توسط آن متد را دارد.
- واژه‌ی کلیدی ref: مقدار آرگومان مزین شده‌ی توسط آن، ممکن است درون متد تنظیم شود، یا خیر و همچنین توسط ارجاع به آن منتقل می‌شود.
- واژه‌ی کلیدی in: مقدار آرگومان مزین شده‌ی توسط آن، درون متد تغییر نخواهد کرد و همچنین توسط ارجاع به آن منتقل می‌شود.

برای مثال اگر پارامترهای value type متد زیر را از نوع in معرفی کنیم، امکان تغییر مقدار آن‌ها درون متد وجود نخواهد داشت:
public static int Add(in int number1, in int number2)
{
   number1 = 5; // Cannot assign to variable 'in int' because it is a readonly variable
   return number1 + number2;
}
و کامپایلر با صدور خطای readonly بودن پارامتر number1، از انجام اینکار جلوگیری می‌کند


واژه‌ی کلیدی جدید in تا چه اندازه‌ای بر روی کارآیی برنامه تاثیر دارد؟

زمانیکه یک value type را به متدی ارسال می‌کنیم، ابتدا به مکان جدیدی از حافظه کپی شده و سپس مقدار clone شده‌ی آن، به متد ارسال می‌شود. با استفاده از واژه‌ی کلیدی in، دقیقا همان ارجاع به مقدار اولیه، به متد ارسال خواهد شد؛ بدون ایجاد کپی اضافه‌تری از آن. برای بررسی تاثیر این عملیات بر روی کارآیی برنامه، می‌توان از BenchmarkDotNet استفاده کرد. برای این منظور ابتدا ارجاعی را به BenchmarkDotNet اضافه می‌کنیم:
<ItemGroup>
     <PackageReference Include="BenchmarkDotNet" Version="0.10.12" />
</ItemGroup>
سپس متدهایی را که قرار است کارآیی آن‌ها بررسی شوند، با ویژگی Benchmark مزین خواهیم کرد:
using BenchmarkDotNet.Attributes;

namespace CS72Tests
{
    public struct Input
    {
        public decimal Number1;
        public decimal Number2;
    }

    [MemoryDiagnoser]
    public class InBenchmarking
    {
        const int loops = 50000000;
        Input inputInstance = new Input();

        [Benchmark(Baseline = true)]
        public decimal RunNormalLoop_Pass_By_Value()
        {
            decimal result = 0M;
            for (int i = 0; i < loops; i++)
            {
                result = Run(inputInstance);
            }
            return result;
        }

        [Benchmark]
        public decimal RunInLoop_Pass_By_Reference()
        {
            decimal result = 0M;
            for (int i = 0; i < loops; i++)
            {
                result = RunIn(in inputInstance);
            }
            return result;
        }

        public decimal Run(Input input)
        {
            return input.Number1;
        }

        public decimal RunIn(in Input input)
        {
            return input.Number1;
        }
    }
}
در آخر برای اجرای آن خواهیم داشت:
static void Main(string[] args)
{
    var summary = BenchmarkRunner.Run<InBenchmarking>();
و در این حالت برنامه را باید توسط دستور «dotnet run -c release» اجرا کرد (اندازه گیری کارآیی در حالت release و نه دیباگ پیش‌فرض)
با این خروجی نهایی:
                      Method |      Mean |    Error |   StdDev | Scaled | Allocated |
---------------------------- |----------:|---------:|---------:|-------:|----------:|
 RunNormalLoop_Pass_By_Value | 280.04 ms | 2.219 ms | 1.733 ms |   1.00 |       0 B |
 RunInLoop_Pass_By_Reference |  91.75 ms | 1.733 ms | 1.780 ms |   0.33 |       0 B |
همانطور که ملاحظه می‌کنید، کارآیی برنامه در حالت استفاده‌ی از پارامترهای in، حداقل 3 برابر شده‌است.


امکان استفاده‌ی از واژه‌ی کلیدی in در حین تعریف متدهای الحاقی

در حین تعریف متدهای الحاقی، واژه‌ی کلیدی in باید پیش از واژه‌ی کلیدی this ذکر شود:
    public static class Factorial
    {
        public static int Calculate(in this int num)
        {
            int result = 1;
            for (int i = num; i > 1; i--)
                result *= i;

            return result;
        }
    }
در این حالت اگر برنامه را به صورت زیر اجرا کنیم (یکبار با ذکر صریح in، بار دیگر بدون in و یکبار هم به صورت فراخوانی متد الحاقی بر روی عدد):
int num = 3;
Console.WriteLine($"(in num) -> {Factorial.Calculate(in num)}");
Console.WriteLine($"(num) -> {Factorial.Calculate(num)}");
Console.WriteLine($"num. -> {num.Calculate()}");
خروجی‌های ذیل را دریافت خواهیم کرد:
(in num) -> 6
(num) -> 6
num. -> 6
به عبارتی حین فراخوانی و استفاده‌ی از متدی که پارامتر آن به صورت in تعریف شده‌است، ذکر in ضروری نیست.

و به طور کلی استفاده‌ی از in در مکان‌های ذیل مجاز است:
• methods
• delegates
• lambdas
• local functions
• indexers
• operators
 

محدودیت‌های استفاده‌ی از پارامترهای in

الف) محدودیت استفاده از پارامترهای in در تعریف overloads
مثال زیر را در نظر بگیرید:
    public class CX
    {
        public void A(Input a)
        {
            Console.WriteLine("int a");
        }

        public void A(in Input a)
        {
            Console.WriteLine("in int a");
        }
    }
در اینجا overloadهای تعریف شده‌ی متد A تنها در ذکر واژه‌ی کلیدی in یا modifier متفاوت هستند.
اگر سعی کنیم وهله‌ای از این کلاس را ایجاد کرده و از متدهای A آن استفاده کنیم:
    public class Y
    {
        public void Test()
        {
            var inputInstance = new Input();
            var cx = new CX();
            cx.A(inputInstance); // The call is ambiguous between the following methods or properties: 'CX.A(Input)' and 'CX.A(in Input)'
        }
    }
خطای کامپایلر مبهم بودن متد A مورد استفاده صادر خواهد شد. یعنی نمی‌توان overload ایی را تعریف کرد که تنها در modifier از نوع in با دیگری متفاوت باشد؛ چون ذکر in در حین فراخوانی متد، اختیاری است.

ب) پارامترهای از نوع in را در متدهای iterator نمی‌توان استفاده کرد:
public IEnumerable<int> B(in int a) // Iterators cannot have ref or out parameters
{
   Console.WriteLine("in int a");
   yield return 1;
}

ج) پارامترهای از نوع in را در متدهای async نمی‌توان استفاده کرد:
public async Task C(in int a) // Async methods cannot have ref or out parameters
{
   await Task.Delay(1000);
}


تاثیر کار با متدهای داخلی تغییر دهنده‌ی وضعیت یک struct

مثال زیر را درنظر بگیرید. به نظر شما خروجی آن چیست؟
using System;

namespace CS72Tests
{
    struct MyStruct
    {
        public int MyValue { get; set; }

        public void UpdateMyValue(int value)
        {
            MyValue = value;
        }
    }

    public static class TestInStructs
    {
        public static void Run()
        {
            var myStruct = new MyStruct();
            myStruct.UpdateMyValue(1);

            UpdateMyValue(myStruct);

            Console.WriteLine(myStruct.MyValue);
        }

        static void UpdateMyValue(in MyStruct myStruct)
        {
            myStruct.UpdateMyValue(5);
        }
    }
}
در اینجا اگر متد TestInStructs.Run را اجرا کنیم، خروجی آن، نمایش عدد 1 خواهد بود.
در ابتدا مقدار struct را به 1 تنظیم و سپس ارجاع آن‌را به متدی دیگر که مقدار آن‌را به 5 تنظیم می‌کند، ارسال کردیم. در این حالت برنامه بدون مشکل کامپایل و اجرا می‌شود.  علت اینجا است که کامپایلر #C زمانیکه متدی را در داخل یک struct فراخوانی می‌کند، یک clone از آن struct را ایجاد کرده و متد را بر روی آن clone اجرا می‌کند؛ چون نمی‌داند که آیا این متد وضعیت و مقدار این struct را تغییر می‌دهد یا خیر. در این حالت کپی اصلی بدون تغییر باقی می‌ماند (در نهایت عدد 1 را مشاهده خواهیم کرد)، اما در آخر فراخوان، ارجاعی از struct را دریافت نکرده و بر روی کپی آن کار می‌کند. بنابراین مزیت بهبود کارآیی، از دست خواهد رفت.

البته در اینجا اگر می‌خواستیم مقدار MyValue را مستقیما تغییر دهیم، کامپایلر از آن جلوگیری می‌کرد و این کد هیچگاه کامپایل نمی‌شد:
static void UpdateMyValue(in MyStruct myStruct)
{
   myStruct.MyValue = 5; // Cannot assign to a member of variable 'in MyStruct' because it is a readonly variable
   myStruct.UpdateMyValue(5);
}