.NET’de çok küçük bir sınıf olan ve şimdilik 2 Özelliği bulunan bu Lazy sınıfı ile ihtiyaç duyduğumuz verinin ihtiyaç anında erişilebilmesi veya yapılacak işlemin ihtiyaç anında yapılması gibi durumlarda kullanabileceğimiz bu sınıf ile performans konusunda projenize katkı sağlayabilirsiniz.
.NET Lazy Sınıfı Nedir? ve Neden Kullanırız?
Lazy sınıfını bazı işlemleri geciktirmemizi ve böylelikle uygulamamamızın açılış süresini kısaltmaktadır. Aslında işlemi şuna benzetebiliriz EntityFramework kullananlar Where() ile query yazdığında Profiler’dan(Sql Server) yada Diagnostic Tools(Visual Studio) aracından baktığımızda herhangi bir sql isteği oluşturmadığını görüyoruz bu istek bazı linq metodları kullandığımızda oluşmaktadır. Bunlar FirstOrDefault(), SingleOrDefault(), ToList() v.b. metodlardır burada kurgu olarak benzer ama kullanım amacı farklı olmaktadır ama olayı anlama noktasında bu basit örnekle yola çıkıldığında daha anlaşılı olacağı kanaatindeyim.
Şimdi Generic Lazy sınıfının içeriğine biraz gözatalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | #region Assembly mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 // C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\mscorlib.dll #endregion using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; namespace System { // // Summary: // Provides support for lazy initialization. // // Type parameters: // T: // The type of object that is being lazily initialized. [ComVisible( false )] [DebuggerDisplay( "ThreadSafetyMode={Mode}, IsValueCreated={IsValueCreated}, IsValueFaulted={IsValueFaulted}, Value={ValueForDebugDisplay}" )] [DebuggerTypeProxy( typeof( System.System_LazyDebugView<> ) )] public class Lazy<T> { // // Summary: // Initializes a new instance of the System.Lazy`1 class. When lazy initialization // occurs, the default constructor of the target type is used. public Lazy(); // // Summary: // Initializes a new instance of the System.Lazy`1 class. When lazy initialization // occurs, the specified initialization function is used. // // Parameters: // valueFactory: // The delegate that is invoked to produce the lazily initialized value when it // is needed. // // Exceptions: // T:System.ArgumentNullException: // valueFactory is null. public Lazy( Func<T> valueFactory ); // // Summary: // Initializes a new instance of the System.Lazy`1 class. When lazy initialization // occurs, the default constructor of the target type and the specified initialization // mode are used. // // Parameters: // isThreadSafe: // true to make this instance usable concurrently by multiple threads; false to // make the instance usable by only one thread at a time. public Lazy( bool isThreadSafe ); // // Summary: // Initializes a new instance of the System.Lazy`1 class that uses the default constructor // of T and the specified thread-safety mode. // // Parameters: // mode: // One of the enumeration values that specifies the thread safety mode. // // Exceptions: // T:System.ArgumentOutOfRangeException: // mode contains an invalid value. public Lazy( LazyThreadSafetyMode mode ); // // Summary: // Initializes a new instance of the System.Lazy`1 class. When lazy initialization // occurs, the specified initialization function and initialization mode are used. // // Parameters: // valueFactory: // The delegate that is invoked to produce the lazily initialized value when it // is needed. // // isThreadSafe: // true to make this instance usable concurrently by multiple threads; false to // make this instance usable by only one thread at a time. // // Exceptions: // T:System.ArgumentNullException: // valueFactory is null. public Lazy( Func<T> valueFactory, bool isThreadSafe ); // // Summary: // Initializes a new instance of the System.Lazy`1 class that uses the specified // initialization function and thread-safety mode. // // Parameters: // valueFactory: // The delegate that is invoked to produce the lazily initialized value when it // is needed. // // mode: // One of the enumeration values that specifies the thread safety mode. // // Exceptions: // T:System.ArgumentOutOfRangeException: // mode contains an invalid value. // // T:System.ArgumentNullException: // valueFactory is null. public Lazy( Func<T> valueFactory, LazyThreadSafetyMode mode ); // // Summary: // Gets a value that indicates whether a value has been created for this System.Lazy`1 // instance. // // Returns: // true if a value has been created for this System.Lazy`1 instance; otherwise, // false. public bool IsValueCreated { get; } // // Summary: // Gets the lazily initialized value of the current System.Lazy`1 instance. // // Returns: // The lazily initialized value of the current System.Lazy`1 instance. // // Exceptions: // T:System.MemberAccessException: // The System.Lazy`1 instance is initialized to use the default constructor of the // type that is being lazily initialized, and permissions to access the constructor // are missing. // // T:System.MissingMemberException: // The System.Lazy`1 instance is initialized to use the default constructor of the // type that is being lazily initialized, and that type does not have a public, // parameterless constructor. // // T:System.InvalidOperationException: // The initialization function tries to access System.Lazy`1.Value on this instance. [DebuggerBrowsable( DebuggerBrowsableState.Never )] public T Value { get; } // // Summary: // Creates and returns a string representation of the System.Lazy`1.Value property // for this instance. // // Returns: // The result of calling the System.Object.ToString method on the System.Lazy`1.Value // property for this instance, if the value has been created (that is, if the System.Lazy`1.IsValueCreated // property returns true). Otherwise, a string indicating that the value has not // been created. // // Exceptions: // T:System.NullReferenceException: // The System.Lazy`1.Value property is null. public override string ToString(); } } |
Generic Lazy sınıfının aslında hiçte komples bir yapıya sahip olmadığını görüyoruz kullanacağımız 2 Property var bunları işlev olarak şöyle anlatabiliriz IsValueCreated özelliği T generic tipi için vermiş olduğunuz tipin oluşturulup oluşturulmadığı ile ilgili true/false değeri döndürmektedir.
Value özelliği ise T generic tipimizi temsil etmektedir. Value özelliğine eriştiğiniz an T tipi Activator tarafından Create edilmektedir fakat eğer Constructor‘a Func<T> ile uyuşan bir metod verirseniz o metod Value özelliğine erişmeye çalıştığınızda tetiklenecek.
Şimdi basit bir yapıya sahip olan bu Lazy konusu için bir örnek yapalım.
Yeni bir C# konsol projesi oluşturuyoruz.
Basit Tiple Lazy Kullanımı
İlk örneğimiz gurgumuz şu şekilde olacak Person adında bir sınıfımız var ve Generic Lazy sınıfından Person tipi ile bir örnek alacağız. Person varsayılan yapıcı metoduna girildiğinde “Person consturtor’i çalıştı.” diye bir metin basacağız konsol ekranına böylelikle hangi aşamada çalıştığını göreceğiz.
1 2 3 4 5 6 7 8 9 10 11 12 | class Person { public string Name { get; set; } public string Surname { get; set; } public IEnumerable<string> Skill { get; set; } public Person() { WriteLine("Person consturtor'i çalıştı."); Skill = new List<string> {"C#", ".NET", "Xamarin"}; } } |
Main metodu içerisindeki basit bir tiple Lazy tanımı yapıyoruz. Tanımımız şu şekilde.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //Generic Lazy sınıfından Person tipiyle bir örnek alıyoruz. Lazy<Person> res = new Lazy<Person>(); //Sınıfın oluşup oluşmadığı kontrol ediliyor WriteLine("Oluştumu: " + res.IsValueCreated); //Value özelliği ile Person sınıfımızdaki Skill adlı propert'iye erişiyoruz //ve Person sınıfının oluşturulması tetikleniyor. WriteLine(res.Value.Skill.Count()); //Sınıfın oluşup oluşmadığı kontrol ediliyor WriteLine("Oluştumu: " + res.IsValueCreated); //Tekrar özelliğe erişiliyor ama bu defa constructor //tetiklenmiyor çünkü daha önce örneği alındı Person nesnesinin WriteLine(res.Value.Skill.Count()); ReadLine(); |
Şimdi yazdığımız kodu adım adım yorumlayalım.
- 2.satırda Lazy sınıfının generic tipine Person tipimizi vererek bir örnek aldık.
- 5.satırda res.IsValueCreated ifadesi ile Person nesnemizin oluşup oluşmadığını sorguladık false döndü nedenine henüz Value özelliğine ihtiyaç duyup kullanmadık ne zaman value özelliğini kullanırsak o zaman Person nesnesi oluşacak.
- 9.satırda artık Value özelliği olan Person nesnemize ihtiyacımız olduğunu belirtiyoruz ve Skill adlı IEnumerable<string> tipli özelliğimizde Count() linq metodu ile kaç kayıt olduğunu konsol ekrarnına basıyoruz. Bu arada Value özelliğini ilk defa kullandığımız için varsayılan kurucu metod çalıştı ve konsol ekranına “Person consturtor’i çalıştı.” metnini yazdı diğer işlemi olan Skill özelliğimize 3 eleman ekledik.
- 12.satırda tekrar nesnenin oluşup oluşmadığını kontrol ediyoruz daha önce Value özelliğine eriştiğimiz için nesnenin oluştuğunu biliyoruz ve true değeri dönüyor.
- 16.satırda Value özelliğini kullanarak tekrar Skill özelliğimizin count sayısını alıyoruz burada gördüğümüz gibi ikinci defa Person‘i create etmedi çünkü daha önce oluşturuldu bu tip Lazy sınıfından aldığımız örneğimiz içerisinde.
Çıktıyı şimdide konsol ekranında görelim.
Çıktı
İlk örneğimiz için kodların tümü şu şekilde.
C#(LazyTest.cs)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | using System; using System.Collections.Generic; using System.Linq; using static System.Console; namespace ConsoleApplication1 { class LazyTest { public static void Main() { //Generic Lazy sınıfından Person tipiyle bir örnek alıyoruz. Lazy<Person> res = new Lazy<Person>(); //Sınıfın oluşup oluşmadığı kontrol ediliyor WriteLine("Oluştumu: " + res.IsValueCreated); //Value özelliği ile Person sınıfımızdaki Skill adlı propert'iye erişiyoruz //ve Person sınıfının oluşturulması tetikleniyor. WriteLine(res.Value.Skill.Count()); //Sınıfın oluşup oluşmadığı kontrol ediliyor WriteLine("Oluştumu: " + res.IsValueCreated); //Tekrar özelliğe erişiliyor ama bu defa constructor //tetiklenmiyor çünkü daha önce örneği alındı Person nesnesinin WriteLine(res.Value.Skill.Count()); ReadLine(); } } class Person { public string Name { get; set; } public string Surname { get; set; } public IEnumerable<string> Skill { get; set; } public Person() { WriteLine("Person consturtor'i çalıştı."); Skill = new List<string> {"C#", ".NET", "Xamarin"}; } } } |
Metod Tanımıyla Lazy Kullanımı
Aslında Lazy‘nin ihtiyaç duyulacağı kısım olarak burayı görüyorum. Nedeni ise çoğu zaman veritabanından gelen veri listesi yada statik olarak kodlarımızda yer alan bazı listeler üzerinde çalışırız ve bazı durumlarda bu liste yükleme işini uygulama ilk başladığında yaparız haliyle buda bir gecikmeye sebep olur. Burada şöyle bir kurgu ile uygulama açılışı daha hızlı hale getirebiliriz örneğin giriş yapmış bir kullanıcının Rollerini global bir özelliğe ataması yapılıyor ve ihtiyaç duyulduğunda kullanılıyor ama yükleme işi uygulama başlangıcında yapılıyor yine uygulama başlangıcından yapılsın ama Lazy ile yapılsın nedenide Lazy ile tanımlandığında veriye uygulama açılışında ihtiyaç duymayıp daha sonraki aşamalarda ihtiyaç duyuyorsak haliyle uygulamada bir performans artışı olacaktır uygulama başlangıcı için olan kurgumuzda.
Şimdi fazla konuşmadan hemen kodlamaya geçeyim 🙂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //IEnumerable<Person> tipindeki Lazy tanımı yapıldı ve //geri dönüş tipi IEnumerable<Person> olan metodumuz parametre olarak verildi. var res = new Lazy<IEnumerable<Person>>(GetData); //IEnumerable<Person> nesnesinin //daha önce oluşturulup oluşturulmadığı kontrol ediliyor. WriteLine($"Oluşturuldumu: {res.IsValueCreated}"); //IEnumerable listesine erişiyoruz. WriteLine($"Kayıt Sayısı: {res.Value.Count()}"); //IEnumerable<Person> nesnesinin //daha önce oluşturulup oluşturulmadığı tekrar kontrol ediliyor. WriteLine($"Oluşturuldumu: {res.IsValueCreated}"); //IEnumerable listesine tekrar erişiyoruz. WriteLine($"Kayıt Sayısı: {res.Value.Count()}"); ReadLine(); |
Bir önceki örneğimizden tek farkı geriye bir IEnumerable<Person> türü dönüyor olması ve constructor metoduna geri dönüş tipi IEnumerable<Person> türünde olan bir metodu veriyor olmamdır. Şimdi konsol çıktısına gözatalım.
Çıktı
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //IEnumerable<Person> tipindeki Lazy tanımı yapıldı ve //geri dönüş tipi IEnumerable<Person> olan metodumuz parametre olarak verildi. var res = new Lazy<IEnumerable<Person>>(GetData); //IEnumerable<Person> nesnesinin //daha önce oluşturulup oluşturulmadığı kontrol ediliyor. WriteLine($"Oluşturuldumu: {res.IsValueCreated}"); //IEnumerable listesine erişiyoruz. WriteLine($"Kayıt Sayısı: {res.Value.Count()}"); //IEnumerable<Person> nesnesinin //daha önce oluşturulup oluşturulmadığı tekrar kontrol ediliyor. WriteLine($"Oluşturuldumu: {res.IsValueCreated}"); //IEnumerable listesine tekrar erişiyoruz. WriteLine($"Kayıt Sayısı: {res.Value.Count()}"); ReadLine(); |
Burada arkaplanın nasıl işlediği konusunda konuşalım daha önce belirttiğim gibi biz Value nesnesine eriştiğimiz an Activator ile T generic tipine vermiş tipimiz create edilmiş oluyor ve GetData adlı metodumuz ise invoke edilerek geriye dönen değer Value‘ya aktarılıyor. Diğer satırları bir önceki örnekte anlattığım için tekrar deyinmeyeceğim ama şunuda belirtip makaleye son vereyim GetData içerisine Value özelliğine ilk erişmemizde giriyor sonraki erişimlerde artık GetData metoduna giriş yapmıyor.
2.Örneğimiz İçin Tüm Kodlar
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | using System; using System.Collections.Generic; using System.Linq; using static System.Console; namespace ConsoleApplication1 { class LazyTest2 { public static void Main() { //IEnumerable<Person> tipindeki Lazy tanımı yapıldı ve //geri dönüş tipi IEnumerable<Person> olan metodumuz parametre olarak verildi. var res = new Lazy<IEnumerable<Person>>(GetData); //IEnumerable<Person> nesnesinin //daha önce oluşturulup oluşturulmadığı kontrol ediliyor. WriteLine($"Oluşturuldumu: {res.IsValueCreated}"); //IEnumerable listesine erişiyoruz. WriteLine($"Kayıt Sayısı: {res.Value.Count()}"); //IEnumerable<Person> nesnesinin //daha önce oluşturulup oluşturulmadığı tekrar kontrol ediliyor. WriteLine($"Oluşturuldumu: {res.IsValueCreated}"); //IEnumerable listesine tekrar erişiyoruz. WriteLine($"Kayıt Sayısı: {res.Value.Count()}"); ReadLine(); } static IEnumerable<Person> GetData() { WriteLine("GetData metodu çalıştı."); return new[] { new Person { Name = "Murat", Surname = "Öner" }, new Person { Name = "XXXX", Surname = "YYYYY" }, new Person { Name = "AAAAA", Surname = "BBBBBB" }, new Person { Name = "CCCCC", Surname = "DDDDD" } }; } } class Person { public string Name { get; set; } public string Surname { get; set; } public Person() { } } } |
[fa class=”fa-github”] Bu makalede anlatılanlar ile ilgili proje için buraya tıklayıp github üzerinden ulaşabilirsiniz.