PDA

View Full Version : آموزش: کد خود را با System.Lazy تنبل کنید (C# 4.0)



mehdi.mousavi
پنج شنبه 30 اردیبهشت 1389, 15:10 عصر
سلام.
این عنوان مقاله ای (http://devlicio.us/blogs/derik_whittaker/archive/2010/05/19/getting-lazy-with-system-lazy.aspx) هستش که در مورد Generic Class ای به اسم System.Lazy (http://msdn.microsoft.com/en-us/library/dd642331%28VS.95%29.aspx) و طرز استفاده از اون در C# 4.0 اشاره میکنه. بطور خلاصه، کار این کلاس این هستش که اجرای کد شما رو تا وقتی که 100% به اجرای اون نیازی نیست، به تعویق بندازه.

موفق باشید.

saed2006
پنج شنبه 30 اردیبهشت 1389, 17:57 عصر
کجا همچین نیازی حس میشه؟
میتونید یه مثال بزنید

Netsky
پنج شنبه 30 اردیبهشت 1389, 18:14 عصر
سلام دوستان .
با تشكر از جناب مهدي موسوي .
بنده هم اين چند خط كوتاه رو از داخل نت پيدا كردم ( واسه دوستاني كه انگليسيشون مثل من خيلي خوب نيست ) :

Lazy Initialiation
با استفاده از این قابلیت، حافظه برای یک شیء تا هنگامی که شیء به حافظه ای احتیاج ندارد، اختصاص نمی یابد. Lazy Initialiation می تواند کارایی برنامه شما را با کنترل روی حافظه های اختصاص داده شده به اشیاء بالا ببرد. شما می توانید این قابلیت را برای هر نوع داده ای با استفاده از (System.Lazy(T فعال کنید.
موفق باشيد ........

saed2006
پنج شنبه 30 اردیبهشت 1389, 18:24 عصر
اگر یه مثال کاربردی که روزمره برای ساخت برنامه هامون باهاش در گیر هستیم مزدین مطلب خیلی روشن تر میشد

odiseh
پنج شنبه 30 اردیبهشت 1389, 19:15 عصر
اجرای کد شما رو تا وقتی که 100% به اجرای اون نیازی نیست، به تعویق بندازه.
موفق باشید.


سلام
در حالت عادي مگه غير از اين حالت هستش كه اومدن اين امكان رو (System.Lazy) رو ايجاد كردند؟

sia_2007
پنج شنبه 30 اردیبهشت 1389, 21:48 عصر
آقای موسوی آیا من مطلب رو اشتباه متوجه شدم ؟


Lazy<IList<Type>> LazyType = new Lazy<IList<Type>>(() => { return TypeFilter(); });
TypeFinderDG MyTypeFinder = new TypeFinderDG(TypeFilter);
Lazy<IList<Type>> LazyType2 = new Lazy<IList<Type>>(() => {return MyTypeFinder() ;});
Func<IList<Type>> GenericMyTypeFinder = new Func<IList<Type>>(TypeFilter);
Lazy<IList<Type>> LazyType3 = new Lazy<IList<Type>>(() => { return GenericMyTypeFinder() ;});
TypeFinderDG MyTypeFinderDG = delegate()
{
List<Type> Types = new List<Type>();
Assembly ASM = this.GetType().Assembly;
IEnumerable<Type> query_Get_ClassesInTheAssembly = ASM.GetTypes().Where(CL => CL.IsClass == true);
foreach (Type Class in query_Get_ClassesInTheAssembly)
{
Types.Add(Class);
}
return Types;
};
Lazy<IList<Type>> LazyType4 = new Lazy<IList<Type>>(() => { return MyTypeFinderDG(); });
Func<IList<Type>> GenericMyTypeFinder2 = delegate()
{
List<Type> Types = new List<Type>();
Assembly ASM = this.GetType().Assembly;
IEnumerable<Type> query_Get_ClassesInTheAssembly = ASM.GetTypes().Where(CL => CL.IsClass == true);
foreach (Type Class in query_Get_ClassesInTheAssembly)
{
Types.Add(Class);
}
return Types;
};
Lazy<IList<Type>> LazyType5 = new Lazy<IList<Type>>(() => { return GenericMyTypeFinder2(); });




private IList<Type> TypeFilter()
{
List<Type> Types = new List<Type>();
Assembly ASM = this.GetType().Assembly;
IEnumerable<Type> query_Get_ClassesInTheAssembly = ASM.GetTypes().Where(CL => CL.IsClass == true);
foreach (Type Class in query_Get_ClassesInTheAssembly)
{
Types.Add(Class);
}
return Types;
}

با Lazy هیچ کدوم از این متدها جز به ضرورت اجرا نمیشن؛ درست
ولی خب قبلا هم همین بود.
من فکر کردم JIT Compiler معجزه کرده؛
آیا اشتباه میکنم ؟

r00tkit
جمعه 31 اردیبهشت 1389, 00:17 صبح
کجا همچین نیازی حس میشه؟
میتونید یه مثال بزنید



In computer programming (http://en.wikipedia.org/wiki/Computer_programming), lazy evaluation is the technique of delaying a computation until the result is required.
ارزیابی تنبل خیلی جا ها بدرد می خوره
باعث افزایش کارایی برنامه می شه چون محاسبات فقط در زمان نیاز انجام می شه
مثال:


static int pow(int x, int y)
{
return x * y * 1000000;
}
static void print(int x, int y)
{
if (x > 0) Console.WriteLine("x>o");
else if(y>0)Console.WriteLine("y>0");
}
static void Main(string[] args)
{
print(pow(10000,1000000), pow(100000,1000000));


}
حالا شما فرض کن اگه کد بالا تنبل باشه : دیگه لازم نیست مقدار y محاسبه بشه

چوم نیازی بهش نداریم
مثال 2)Control structures


if x then y else z
خوب بازم اگه x و yو z یه محاسبه ی سنگین باشن
چقد برنامه سنگین می شه ، فرض کن عبارت تنبل باشه ان وقت اگه ي x درست باشه دیگه نیاز به محاسبه ی z نیست

3) با Lazy evaluation می شه ساختمان داده ها بی نهایت ساخت

4) Demand paging
:
زمانی page از دیسک به رم کپی می شه که تلاشی برای دسترسی بهش صورت بگیره(یعنی زمانی که page fault (http://en.wikipedia.org/wiki/Page_fault) صورت بگیره)

Lazy evaluation در زبان ها به دو صورت هستش یا default هستش مثل haskell یا به صورت explicitly در سینتکس ذکر بشن مثل "delay" و "force" برای scheme یا "lazy"
و "Lazy.force" برای Ocaml




Lazy evaluation refers to how expressions are evaluated when they are passed as arguments to functions and entails the following three points:


The expression is only evaluated if the result is required by the calling function, called delayed evaluation] (http://en.wikipedia.org/wiki/Lazy_EvaluationC#%E2%80%8Eite_note-1)
The expression is only evaluated to the extent that is required by the calling function, called Short-circuit evaluation.
The expression is never evaluated more than once, called applicative-order evaluation.

mehdi.mousavi
جمعه 31 اردیبهشت 1389, 02:06 صبح
سلام.
Lazy Initialization (یه بهتره بگیم Differed Execution) یعنی به تعویق انداختن اجرای کد تا وقتی که حقیقتا نیاز به اجرای اون داشته باشیم.
کدی که در لینک فوق اومده بود رو (اندکی ساده تر کردم و) اینجا قرار میدم تا بتونم در موردش توضیح بدم:


public class OrdersFactory
{
public Order FetchOrder()
{
Console.WriteLine("About to fetch single order");
return new Order { OrderId = 1, InvoiceNumber = 1, OrderAmount = 19.95D, ItemCount = 2 };
}

}


همونطور که می بینید، اینجا یک کلاس داریم به اسم OrdersFactory، که حاوی متودی به اسم FetchOrder هستش که یک Order رو ایجاد میکنه و به ما بر میگردونه. فقط به این مساله دقت کنید، که در ابتدای این تابع، دستور Console.WriteLine نوشته شده تا به ما نشون بده که متود مزبور چه هنگام فراخوانی میشه (طبیعتا وقتی پیام مربوطه توی Console نوشته بشه، این به این معناست که متود مربوطه فراخوانی شده).

کلاس Order رو هم می بینید، که 4 تا Property داره و مطلب عجیبی با اون آمیخته نشده.

public class Order
{
public Int32 OrderId { get; set; }
public Int32 InvoiceNumber { get; set; }
public double OrderAmount { get; set; }
public Int32 ItemCount { get; set; }
}


حالا تو حالت عادی وقتی که متود FetchOrder رو فراخوانی میکنیم، خروجی برنامه پیام

Fetching order info --- non lazy
About to fetch single order

خواهد بود:

class Program
{
static void Main( string[] args )
{
var ordersFactory = new OrdersFactory();

Console.WriteLine("Fetching order info --- non lazy");
var orderAsNonLazy = ordersFactory.FetchOrder();

Console.ReadLine();
}
}



که البته غیر این هم انتظار نداشتیم. حالا بیاییم تابع FetchOrder رو بکمک کلاس Lazy فراخوانی کنیم:


OrdersFactory ordersFactory = new OrdersFactory();
System.Lazy<Order> o = new Lazy<Order>(() => { return ordersFactory.FetchOrder(); });


اینجا انتظار داریم که در خط دوم، وقتی FetchOrder رو فراخوانی کردیم، تابع فوق Call بشه، اما چون Lazy هستش این اتفاق نمی افته.

در عوض، وقتی برنامه به این خط میرسه،

Order order = o.Value;

اینجاست که ما واقعا داریم به اون Order ای که گرفته بودیم دسترسی پیدا می کنیم. پس تازه توی این خط هستش که تابع فوق اجرا میشه، نه اونجایی که FetchOrder رو Call کرده بودیم.

از این مساله میشه جاهای زیادی استفاده کرد و سود برد اما خوب، مشکلاتی رو هم معرفی میکنه، که اگر حواستون به اجرای با تاخیر کد نباشه، ممکنه براتون دردسر ساز بشه.

موفق باشید.

sia_2007
جمعه 31 اردیبهشت 1389, 10:36 صبح
البته گاهی اوقات دکمه تشکر نمیتونه اون چنان که باید تاثیرگزار باشه؛ و هم چنین من میخواستم با این کار تا حد امکان توجه سایر دوستان رو به این مطلب جلب کنم.
منظورم اینه که احساس من این بود که در این پست خاص؛ برخلاف نظر شما؛ تشکر کفایت نمیکرد.
به هر حال مدیر و هم چنین سازنده این پست خوب شمایید؛ و از شما به خاطر اعلان زودتر این موضوع تشکر میکنم.

خب ببینید؛

من یه مثال تقریبا ! کاربردی نوشتم :



private void btnSample_Click(object sender, EventArgs e)
{
PersonManager PManager = new PersonManager();
CompareNonLazy(PManager.FetchPersons(), PManager.FetchPersons_2());
Lazy<List<Person>> PersonListLazy_1 = new Lazy<List<Person>>(() => {return PManager.FetchPersons() ;});
Lazy<List<Person>> PersonListLazy_2 = new Lazy<List<Person>>(() => { return PManager.FetchPersons_2(); });
CompareLazy(PersonListLazy_1, PersonListLazy_2);
}

private void CompareNonLazy(List<Person> PersonList1, List<Person> PersonList2)
{
if (PersonList1.Count > 0)
{
Debug.WriteLine(1);
}
else if (PersonList2.Count > 0)
{
Debug.WriteLine(2);
}
}

private void CompareLazy(Lazy<List<Person>> PersonList1, Lazy<List<Person>> PersonList2)
{
if (PersonList1.Value.Count > 0)
{
Debug.WriteLine(1);
}
else if (PersonList2.Value.Count > 0)
{
Debug.WriteLine(2);
}
}


ببینید در حالت عادی ؛ تمامی پارامترهای ارسالی به یک متد؛ به صورت کاملا قطعی ارسال میشوند.
بنابر این در متد عادی اول؛ هر دو تا personlist ؛ Fetch میشوند ؛ و سپس به متد ارسال میشوند.
اما میبینید که بر اساس شرط if ؛ شاید هیچ وقت نیازی به محاسبه personlist2 نبود؛ اما متاسفانه قبلا محاسبه شده و فشارش رو به سیستم ایجاد کرده بود.

در حالت تنبل؛ هیچ کدوم از پارامترها محاسبه نمیشوند؛ ( امیدوارم اشتباه نکنید ؛ به خاطر متد نیست؛ به خاطر پارامتر ارسالی است؛ که Collection از نوع Lazy Generic List است ).
زمانی که در اول شرط personlist اولی چک میشود؛ تازه مقدارش محاسبه میشود؛ و اگر همان جا خروج کنیم دیگر personlist2 محاسبه نمیشود !

آیا فقط دستوراتی رو که توی Collection Initializer مینویسیم رو میشه تنبل کرد ؟
گاهی وقتها محاسبه یه int ساده هم ممکنه کلی به سیستم فشار بیاره.

saed2006
جمعه 31 اردیبهشت 1389, 11:21 صبح
تاثیر این روش روی سرعت اجرای برنامه چقدر هست؟
کارایی ان کی مشخص میشود؟

علیرضا مداح
جمعه 31 اردیبهشت 1389, 11:38 صبح
سلام،

آیا فقط دستوراتی رو که توی Collection Initializer مینویسیم رو میشه تنبل کرد ؟خیر، به مثال ساده ای که نوشتم دقت کنید:

static void Main(string[] args)
{
Int32 a = Int32.MaxValue;
Int32 b = Int32.MaxValue;
Lazy<Int64> product = new Lazy<Int64>(() => { return Math.BigMul(a, b); });

Console.WriteLine("IsValueCreated = {0}", product.IsValueCreated);

Console.WriteLine("-----");
Console.WriteLine("uhmm, Cool, yeah? The value hasn't been created yet!");
Console.WriteLine("Being 'Lazy' is not always considered bad!");

Console.WriteLine("Value = {0}", product.Value);

Console.ReadLine();
}


ملاحظه می کنید که System.Lazy<T> با هر نوعی ساززگار است و هر زمان که مقدار پروپرتی Value خوانده شده یا متد ToString() فراخوانی شود، عملیات Initialization صورت می پذرید، توجه کنید که اگر کد Initializer با یک Exception مواجه شود، هر زمان که مقدار Value خوانده شود، آن Exception نیز throw خواهد شد، پس بهره گیری از بلوک try--catch را به عنوان یک Best Practice در مواقع مورد نیاز مد نظر قرار دهید، البته نکات دیگری هم در این زمینه وجود دارد،/

mehdi.mousavi
جمعه 31 اردیبهشت 1389, 11:47 صبح
تاثیر این روی سرعت برنامه هست؟

سلام.
ببینید. بستگی داره اجرای چه کدی رو به تعویق بندازید. طبیعتا اگر کد مورد نظر شما، از Resource های سیستمی زیادی استفاده کنه (مثلا توان پردازشی زیاد، حافظه زیاد و ... نیاز داشته باشه)، طبیعتا هر چه دیرتر اجرا بشه، بهتره. اما خوب، نباید فریب این کلاس رو هم بخورید و همه مسائل رو به این چشم ببینید.

فقط جاهایی که کارهای Resource-Intensive می خواهید انجام بدید از این کلاس استفاده کنید.

موفق باشید.

sia_2007
جمعه 31 اردیبهشت 1389, 12:22 عصر
من تونستم سرعت یکی از کدهام رو 15% افزایش بدم. تقریبا
بله من هم به این کد رسیدم؛


Lazy<Int32> A = new Lazy<int>(() => { return 10; });
MessageBox.Show(A.Value.ToString());


ببینید باید تو اونجا که ممکنه از اجرای کدها به صورت بیهوده جلوگیری کرد؛ مخصوصا اگه خواستار منابع زیادی باشند.

حیف که فرصت ندارم؛ وگرنه کامل در موردش تحقیق میکردم.
با این حال ممنون

haamidd
چهارشنبه 14 تیر 1396, 20:15 عصر
مطالب خوبی در این سایت نوشته شده درمورد با موضوع مقاله

http://hadiahmadi.ir/2014/12/csharp-lazy-class/