İlkay İlknur

just a developer...

C# 7.2 - Value Typelarda Referans Semantiği Yenilikleri

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

Visual Studio 15.5 update'inin release olması ile beraber C#'ın bir sonraki minor release'i olan 7.2 versiyonu da artık RTM oldu. Daha önceki C# 7.1 yazımda da bahsettiğim üzere minor C# versiyonları Visual Studio içerisinde default olarak aktif olmuyor ve bu özellikleri kullanabilmemiz için ufak bir ayar yapmamız gerekiyor. Bu gerekli olan ayara da C# 7.1 Yenilikleri yazımdan ulaşabilirsiniz.

C# 7.2'de de yine C# 7.1'de olduğu gibi ufak ve faydalı yenilikler var. Ancak C# 7.2 sadece ufak yeniliklerin olduğu bir release değil. C# 7.2'nin ana teması, value type'larla çalışırken onları pass-by-reference mantığı ile kullanarak value typeların sahip olduğu kopyalanma özelliklerinin önüne geçip allocationı ve kopyalanma operasyonlarını azaltmak. Bu yazımızın konusu da bu tema kapsamında gelen yenilikler. C# 7.2 ile beraber gelen diğer yeniliklere de başka bir yazıda göz atacağız.

Bu Özellikler Neden Gerekliydi ?

Value typelar, özellikle de structlar performans kritik algoritmalarda, oyunlarda, 3D modelleme vs.. gibi alanlarda oldukça fazla kullanılmakta. Bunun en önemli nedeni value typeların heap allocationa neden olmaması nedeniyle Garbage Collector'ın getirebileceği tahmin edilemeyen yüklerden ve sorunlardan olabildiğince kaçınmak. Ancak value typelar bir metoda parametre olarak geçildiklerinde eğer herhangi bir ref parametre kullanılmazsa value typeların bir kopyası yaratılıp geçilir (pass-by-value) . Örneğin, .NET Framework içerisindeki System.Windows.Media.Media3D.Matrix3D structı içerisinde 16 tane double property bulunmakta. Bu da toplamda 128 byte ediyor. Dolayısıyla siz bu structı metotlara parametre olarak geçmeye kalkarsanız bu 128 bytelık struct sürekli olarak kopyalanarak metotlara geçilecektir. Elinizde bu structlardan oldukça fazla olduğunu düşünürseniz, bir metot çağırımı bile oldukça sıkıntılı sonuçlara yol açabilir. Bundan kaçınmak için tabi ki de C# içerisinde bazı çözümler var. Ancak C# 7.2 bu özelliklere ekstra yenilikler ekleyerek özellikle structlarla çalışmayı daha kolay ve özellikle güvenilir hale getiriyor. Dolayısıyla aşağıda anlatmaya çalışacağım yenilikleri bu konseptte değerlendirmekte fayda var.

in Parametreleri

Şu ana kadar C# içerisinde bir value type'ı bir metoda referansıyla parametre olarak geçmek istediğimizde out ve ref parametrelerini kullanabiliyorduk. Böylece value typeların metot çağırılırken kopyalanmasının önüne geçebiliyoruz ve referansıyla gönderebiliyoruz. Bu seçeneklerden, out parametreyi kullandığımızda, out parametrede çağırılan metot bu değişkene mutlaka bir değer ataması yapması gerekirken, ref parametrelerde ise değişken yine referansı ile metoda gelirken metodun değişken üzerinde bir değişiklik yapması zorunlu değil.

static void Out(out int x)
{
    x = 12;
}

static void Ref(ref int x)
{
    //Bazı durumlarda değiştirilebilir.
    if (x > 10)
    {
        x = 9;
    }
}

C# 7.2 ile beraber gelen in parametreleri ise yine değişkenin referansıyla metoda gönderilmesini sağlarken çağrılan metodun bu değişkeni değiştiremeyeceğinden emin olunmasını sağlıyor. Böylece çağırılan metot in parametreyi değiştiremeyeceği için gönderdiğiniz value type'ın özellikle de struct'ın değişmediğinden emin olabilirsiniz.

Örneğin,

static void In(in int x)
{
    x = 12;
}

Yukarıdaki gibi gelen bir in parametreye bir değer ataması yapmak isterseniz derleme hatası alıyorsunuz.

in parametre kabul eden bir metodu çağırmak istediğimizde iki şekilde çağırma yapabilmemiz mümkün. Birincisi out parametrelerde olduğu gibi in parametrenin başına in keywordünü eklemek.

static void Main(string[] args)
{
    int k = 1;
    In(in k);
}

Diğeri ise in keywordünü eklemeden çağırmak. Eğer in parametre yolladığınızı kodu okuyan kişinin bilmesini istiyorsanız parametrenin başına in yazmanız faydalı olabilir. Ama kullanım şekli tamamen size kalmış.

static void Main(string[] args)
{
    int k = 1;

    In(in k);            
    In(k);
}

in parametrelerin en büyük avantajı value typelar ile elde ediliyor.Reference typelarda etkisinin pek olmadığını belirtmemizde fayda var.

ref readonly returns

in parametrelerle, parametre referansıyla ve değiştirelemez bir şekilde metoda parametre geçilirken bu özellikle de dönüş değeri olan value type referansıyla döndürülür ve bu referans üzerinden döndürülen value type değiştirilemez. Bu özelliğin faydalı olabileceği kullanım alanlarında birini kısaca anlatmaya çalışalım.

Örneğin Point isimli bir struct olduğunu düşünelim.

struct Point
{
    public int X { getset; }
    public int Y { getset; }
}

Bunun için Default değerini kapsayacak bir property koymak istersek şu şekilde bir ekleme yapmamız gerekir.

struct Point
{
    public int X { getset; }
    public int Y { getset; }
    public static Point Default => new Point();
}

Burada her Default propertysi kullanıldığında stackde yeni bir Point yaratılır. Halbuki bunun yerine ref readonly olarak döndürürsek tek bir Point yaratıp sürekli olarak bunun referansını döndürebiliriz.

struct Point
{
    public int X { getset; }
    public int Y { getset; }
    private static Point defaultp = new Point();
    public static ref readonly Point Default => ref defaultp;
}

Bu şekilde Point structını tanımladıktan sonra Point üzerinden Default propertysine aşağıdaki gibi ulaşabiliriz.

static void Main(string[] args)
{
    ref readonly var defaultp = ref Point.Default;
}

Bu şekilde bir kullanımda defaultp değişkeni içerisinde readonly referans bulunduğu için Main metot üzerinde defaultp değişkeni üzerinden bir değer ataması yapılması mümkün olmayacaktır ve compiler kodu derlemeyecektir.

static void Main(string[] args)
{
    ref readonly var defaultp = ref Point.Default;
    //ref readonly !!!
    defaultp.X = 1; //---> Mümkün değil !!!
}

ref readonly olarak dönen bir değerin kopyasını almak istersek de dönen değeri ref readonly olmayan bir değişkene atamasını yapmamız yeterli.

static void Main(string[] args)
{
    var defaultp = Point.Default;
    //Default'in yeni bir kopyası oluşturuldu. 
    defaultp.X = 1; //---> Artık mümkün :)
}

Bu özelliği isterseniz metotların dönüş değerleri için de kullanabilirsiniz. Biz örnek olarak property yaptık ancak bu özellik metotların dönüş değerleri için de kullanılabilir.

readonly Structs

C# 7.2 öncesinde readonly olarak tanımlanan bir struct variable'ı üzerinde struct içerisindeki bir instance metodu çağırmak istediğimizde derleyeceği o metodun structı değiştirip değiştirmeyeceğini bilemediği için otomatik olarak structı arka planda geçici bir değişkene atayıp o değişken üzerinden ilgili metodu çalıştırıyordu. Bunun da performans olarak bazı sıkıntıları olabiliyordu. Örnek senaryo olarak https://codeblog.jonskeet.uk/2014/07/16/micro-optimization-the-surprising-inefficiency-of-readonly-fields/ bu yazıyı okuyabilirsiniz. Bu performans sorunları nedeniyle de çoğu zaman readonly kullanmaktan vazgeçilebiliyordu.

C# 7.2 ile beraber struct tanımlamasının başına readonly yazarak structların readonly olmasını sağlayabiliyoruz. Böylece derleyici de yukarıda belirttiğim senaryoda geçici bir değişken yaratmak durumunda kalmıyor ve performans sıkıntılarının da önüne geçilmiş olunuyor.

readonly olarak tanımladığımız structların içindeki değerlere atamalar sadece constructor içerisinde yapılabiliyor. Bunun haricinde başka bir yerde atama yapmak mümkün değil.

readonly struct Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }       
}

Şimdilik bu tema kapsamında bu yazıda bahsedeceklerimiz bu kadar. C# 7.2 ile beraber gelen ve yine tema kapsamında olan Span< T>'nin kullanımından ise başka bir yazıda bahsediyor olacağım. Bu yeniliklerin özellikle yüksek performanslı sistemlerin, oyunların optimizasyonu amacıyla yapıldığını hatırlatmakta fayda var. Biz belki kod yazarken sürekli structları veya bu yenilikleri kullanmıyor olabiliriz ancak kullandığımız frameworkler, toollar bu tipleri arka planda kullanıyorlar. Dolayısıyla bu yenilikler doğrudan olmasa bile dolaylı yoldan bizleri etkiliyor olacak. Mesela buradaki pull request içerisinde corefx içerisinde bu değişikliklerin adapte edildiğini görebilirsiniz.

Bir sonraki yazıda görüşmek üzere,



C# 7.1 Yenilikleri

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

Daha önceki blog yazılarımda ve videolarımda C#'ın minor releaselerinin çıkarılması için çalışmalar yapıldığından bahsetmiştim. Şimdiye kadar baktığımızda her yeni Visual Studio versiyonu ile beraber C#'ın da yeni versiyonu çıkıyordu. Bu nedenle hem büyük kapsamlı yenilikler hem de ufak yenilikler doğrudan tek bir versiyon içerisinde bulunuyordu. Halbuki dil içerisindeki bazı yenilikler geliştirilmesi bitse bile yeni versiyon çıkana kadar biz developerlarla buluşamıyordu.

İşte C# 7.1, bu noktada C#'ın ilk minor release'i olmakta. Visual Studio 2017 kullananlar için 15.3 versiyonu ile beraber gelirken, .NET Core'da ise 2.0 SDK'i ile beraber geliyor. Ancak bu versiyonları edinseniz bile C# 7.1 yenilikleri otomatik olarak kapalı geliyor. Bu nedenle ayrıca açmak için ufak bir ayar yapmaya ihtiyacımız var. Bunun için projemize sağ tıklayıp, Properties seçeneğini seçiyoruz ve sonra da çıkan ekrandan Build tabına geçiyoruz. En altta bir Advanced butonu olacak ve o butona tıkladığımızda ise şöyle bir ekran karşımıza gelecek.

Gördüğünüz gibi Language Version olarak latest major versiyon seçili. Yani default olarak C# 7.0 kullanılıyor. Language version seçeneklerini açtığımızda ise şu şekilde bir liste ile karşılaşıyoruz.

Buradan eğer "latest minor version" seçeneğini seçersek projede artık release olan tüm C# minor versiyonlar updateler geldikçe otomatik olarak kullanımda olacaktır. Bunları yapmak yerine belirli C# versiyonunu seçip projede hep o versiyonun kullanılmasını da sağlayabilirsiniz. Bu ayarı yaparken unutulmaması gereken bir nokta da bu ayarın build konfigürasyonu başına yapılması. Yani yaptığınız değişikliğin hem debug hem de release modda yapılmış olması gerekiyor. Eğer csproj dosyasını manuel editleyerek yapmak isterseniz bunu göz önüne almayı unutmayın. Eğer Visual Studio üzerinden yukarıdaki gibi ayar yapmak istiyorsanız da projeye sağ tıklayıp Properties'e tıkladığınızda gelen ekranda sol yukarıdaki Configuration combobox'ında All Configurations'ı seçmeyi unutmayın.

Şimdi gelelim C# 7.1 özelliklerine...

Async Main

C# 5.0 ile beraber async/await geldiğinden beri özellikle demo yaparken Main metodunun async olamaması ile ilgili hep bir istisna olduğundan bahsediyorduk. Methodu async yapamadığımız için de async bir metot çağırırken bazı farklı kullanımlarda bulunuyorduk. Artık yıllar sonra artık bu durum sona erdi ve Main metot Task veya Task<int> döndürebilir hale geldi. Main metot içerisinde async metotları gönül rahatlığıyla artık await edebiliriz 😃

Default Literals

C# içerisinde bir tipin default değerini alabilmek için default expressionları kullanabiliyorduk.

Örneğin,

var x = default(string);
var y = default(Func<stringintList<string>>); // <---- :(

Sadece string gibi tiplerin default değerini alırken yazım olarak çok zorlanmasak da özellikle kompleks generic tipler olduğunda onları yazmak bazen zor olabiliyordu. Yukarıda verdiğim ikinci örnek bu zorluğa ufak bir örnek olabilir. C# 7.1 ile beraber compiler tipini tahmin edebilidiği durumlarda artık sizin default a ekstra bir tip vermenizi zorunlu kılmıyor. Bu nedenle doğrudan default literal'i kullanabiliyorsunuz. 1-2 kullanım yaparsak örnek olarak...

static async Task Main(string[] args)
{
    Foo(default, 12);
}

static int Foo()
{
    return default;
}

static void Foo(string k, int y)
{

}

Yukarıda görüldüğü gibi metotların alacakları parametrelerde, döndüreceği değerlerde ve daha başka pek çok noktada sadece default literalini kullanabiliriz. Burada kritik nokta compilerın default değerini alacağı tipi tahmin edebilmesi. Tahmin edemediği durumlarda zaten sizden tipi ayrıca belirtmenizi isteyecektir.

Tuple isimlendirme geliştirmeleri

C# 7.0 ile beraber gelen benim en favori özelliğim tuplelar. C# 7.1 ile beraber de tuple içerisindeki fieldların isimlendirilmesi ile ilgili ufak ama güzel bir yenilik geliyor. Tuple kullanımında tuple yaratırken fieldlara dışarıdan isim verebiliyorduk.

Örneğin,

var count = 5;
var sum = 50;
var retVal = (sum: sum, count: count);

Bu şekilde kullanımlarda tuple içerisine koyduğumuz değişkenlerin isimleriyle tuple içerisindeki fieldların ismini büyük oranda aynı oluyor. Dolayısıyla aslında bunu biz belirtmesek de compiler bunu arka planda kendisi yapsa nasıl olur 😃 C# 7.1 ile artık bu mümkün

var count = 5;
var sum = 50;
var retVal = (sum, count);

Console.WriteLine($"Count:{retVal.count} , Sum:{retVal.sum}");

Yukarıda gördüğünüz gibi tuple yaratırken içerisine verdiğimiz değişkenlerin isimleriyle aynı isimde fieldlar yaratılıyor.

Gördüğünüz gibi C# 7.1 içerisinde ufak ama günlük hayatımızda kod yazarken bize yardımcı olacak özellikler gelmekte. Bir sonraki release olacak olan 7.2 release'inde çok daha farklı yenilikler geliyor olacak. Artık bu özellikler için 2 yıl yeni Visual Studio versiyonunu beklemeye gerek yok.



C# 7.0 - Digit Separators

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

Not : Bu makaledeki örnekleri denemek için gereken ortam kurulum bilgisini buradan alabilirsiniz.

C# 7.0 ile beraber gelmesi planlanan ufak özelliklerden biri de digit separatorler. Digit separatorler ile numeric değer tanımlamaları sırasında istediğiniz bölümde basamakları ayırabiliyorsunuz. Böylece kod yazarken tanımlamış olduğunuz numeric değişkenlerin taşıdıkları değerler daha okunabilir olurken hem de siz tanımlama esnasında basamakları ayırabildiğiniz için hata yapma olasılığınız daha düşük oluyor. Digit separator olarak ise _ kullanıyoruz.

Örneğin,

int x = 1_000_000;
double y = 1_00.0_9;

Digit separatorlerin bir diğer kullanım alanı da binary literal'lar. Binary literal tanımlamaları esnasında da digit separatorleri kullanmamız mümkün.

Örneğin,

int x = 0b11_00_0_01;
double y = 0b1_00;

Bu ufak özellikle ilgili yazımızda bu kadar. Bir sonraki C# 7.0 özelliğinde görüşmek üzere...



C# 7.0 - Binary Literals

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

Not : Bu makaledeki örnekleri denemek için gereken ortam kurulum bilgisini buradan alabilirsiniz.

C# 7.0 ile beraber gelmesi muhtemel özelliklerden biride Binary Literals özelliği. Aslında C# design meetinglerini takip edenler için bu özellik süpriz bir özellik değil. Çünkü aslında aynı özellik C# 6.0'da da planlanıyordu ancak release döneminde bu özellik ne yazık ki C# 6.0'a dahil edilmedi.

İşin magazin tarafını bir kenara bırakırsak :) binary literals ile kod içerisinde binary olarak tanımlamalar yapabiliyorsunuz. Bunu yapmak için binary ifadenin başına 0b veya 0B yazmanız yeterli.

Örneğin,

static void Main(string[] args)
{
    int x = 0b1100001;
    double y = 0b100;
}

Gördüğünüz gibi kullanımı oldukça basit. Gelelim bu özellik nerelerde işimize yarayacak kısmına. Enumlara Flags attribute'ünü ekleyip 2'nin üssü bir biçimde değerler verdiğimizde enumlar üzerinde bitwise operasyonlar yapabiliyoruz. (Enumlar üzerinde bitwise operasyonlar ile ilgili yazmış olduğum yazıyı inceleyebilirsiniz. http://www.ilkayilknur.com/coklu-enum-degerleriyle-calismak) Burada 2'nin üssü değerleri verirken binary literalleri kullanabiliriz. Böylece hızlı bir şekilde ve daha az hata olasılığıyla hızlı bir şekilde kodumuzu yazabiliriz.

[Flags]
enum Colors
{
    Red = 0b1,
    Green = 0b10,
    Blue = 0b100
}

Gördüğünüz gibi binary literallerin bana göre en önemli kullanım alanı burası olacak. Bunun yanında C# ekibi binary ifadeleri öğrenen developerların da C# içerisinde eğitim amaçlı binary literalleri kullanacaklarını düşünüyorlar.

C# 7.0 binary literals özelliğiyle ilgili yazımız bu kadar. Zaten gördüğünüz gibi oldukça ufak bir özellik :) Bir sonraki yazıda görüşmek üzere...



C# 7.0 - Local Functions

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

Not : Bu makaledeki örnekleri denemek için gereken ortam kurulum bilgisini buradan alabilirsiniz.

C# 7.0 ile beraber gelmesi muhtemel en basit özelliklerden biride local functions özelliği. Hemen örnek yapmadan önce ilk olarak bu özelliğe ne gerek vardı sorusunun cevabını vermeye çalışalım.

Kod yazarken metotların çok fazla uzadığı ve bakımının zorlaştığı senaryolarda metot içerisindeki fonksiyonel bölümleri ayrı bir metot yaparız ve ilgili metot içerisinden de dışarıya aldığımız diğer metodu çağırırız. Bu ufak helper metotlarımız aslında baktığımızda sadece tek bir metot tarafından kullanılırlar ancak tanımlandıkları class'ın bir üyesi olurlar. Dolayısıyla bu helper metotları private olarak tanımlasak bile herhangi bir developer class içerisindeki başka bir metot içerisinden de istemememize rağmen bu metodu çağırabilir.

class Foo
{
    void Bar()
    {
        List<int> list = new List<int>();
        //Do something
        HelperMethod();
    }

    private void HelperMethod()
    {

    }
}

Örneğin yukarıdaki kodda HelperMethod isimli metot aynı class içerisindeki başka bir metottan da çağırılabilir.

class Foo
{
    void Bar()
    {
        List<int> list = new List<int>();
        //Do something
        HelperMethod();
    }

    private void HelperMethod()
    {

    }

    void Bar2()
    {
        HelperMethod();
    }
}

Bu aslında istenmeyen birşey olsa da bunun önüne geçmek pek mümkün değil. Tek kaçış yolu lambda ifadesi kullanarak Func veya Action tipinde bu helper metotları tanımlamak.

class Foo
{
    void Bar()
    {
        List<int> list = new List<int>();
        Action helper = () => { };
        //Do something
        helper();
    }
}

Bu durumda helper kısmı lambda ifadasi ile tanımladığımız için bu lambda ifadesi sadece Bar metodu içerisinden çağırılabilir ve erişilebilir. Böylece aslında en baştan amaçladığımız şeyi gerçekleştirmiş oluruz. Ancak Action ve Func olarak tanımlamanın bazı kısıtlamaları ile performans ve memory bakımından da bazı dezavantajları var.

Sahip oldukları kısıtlamalardan bazıları

  • Lambda ifadeleri generic olarak tanımlanamaz.
  • Lambda ifadeleri içerisinde ref, params ve out olarak parametre tanımlayamayız.
  • Lambda ifadelerini recursive olarak çağırmak için ufak bir trick yapmak gerekiyor. Tanımlama esnasında kendisini çağıramaz.

Tüm bu sıkıntılardan ötürü C# 7.0'da local functions özelliğinin getirilmesi planlanıyor. Şimdi gelelim local functions özelliğinin kullanımına.

class Foo
{
    void Bar()
    {
        List<int> list = new List<int>();
        void HelperMethod()
        {

        }

        HelperMethod();
    }
}

Yukarıda da gördüğünüz gibi metot içerisinde yeni bir metot tanımlamaktan başka birşey yapmıyoruz aslında. Dolayısıyla out, ref ve params parametrelerini de kullanabiliyoruz. Ayrıca bu metotların çağırımında da herhangi bir ekstra performans kaybı veya ekstra memory kullanımı da olmuyor. Local functionlar sadece tanımlı bulundukları metot içerisinden erişilebilir durumdalar. Yani başka bir metot içerisinden local function'ın çağırılması gibi bir durum söz konusu da değil. Yukarıdaki kodda da gördüğünüz gibi bir local functionı çağırmadan önce yukarıda o local function'ı tanımlamış olmak gerekiyor. Ayrıca local functionda, içerisinde bulunduğu metot içerisindeki her elemana da ulaşabilirsiniz.

class Foo
{
    void Bar()
    {
        List<int> list = new List<int>();
        void HelperMethod()
        {
            foreach (var item in list)
            {

            }
        }

        HelperMethod();
    }
}

C# 6.0 ve C# 7.0 Birarada

Yazıyı sonlandırmadan önce C# 6.0'da gelen expression-bodied function members özelliğini kullanarak local functions tanımlamadan bahsetmek istiyorum. Böylece tek satırlık bir local functionınız olduğunda hızlı bir şekilde bu özelliği kullanarak local functions tanımlayabilirsiniz.

class Foo
{
    void Bar()
    {
        List<int> list = new List<int>();
        void HelperMethod() => Console.WriteLine("Hello");

        HelperMethod();
    }
}

Evet, local functionsla ilgili bu yazıda bahsedeceklerimiz bu kadar. Gördüğünüz gibi ufak ama faydalı bir özellik gibi duruyor. Tabi C# developerların bu özelliği ne kadar benimseyeceklerini de merak etmiyor değilim.

Bir sonraki yazıda görüşmek üzere...



C# 7.0 Özelliklerini Nasıl Test Ederiz ?

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

Bir önceki blog yazısında C# 7.0 yeniliklerinden bahsettiğim videomu paylaşmıştım. Diyelim ki videoyu izlediniz veya başka yerlerden okuyup heyecanlandınız ve C# 7.0 özelliklerini test etmek istiyorsunuz :) Bu özellikleri test edebilmeniz için önünüzde 2 yol var. Biri kısmen kolay olan yol diğeri ise oldukça zorlu :) Önce kolay olandan başlayalım.

Visual Studio 15 Preview Üzerinde Test Etmek

Şu anda C# 7.0 özelliklerini test etmenin en kolay yolu Visual Studio 15 Preview kullanmak. Visual Studio 15 Preview sürümü adı üzerinde henüz preview olduğu için production makinanıza kurmanızı kesinlikle tavsiye etmiyorum. Bu yüzden bilgisayarınızda bir sanal makina yaratıp orada Visual Studio 15'i kurup testlerinizi yapabilirsiniz. Bir diğer alternatif ise Azure üzerinde Visual Studio 15 Preview imajlı bir VM yaratmak.

Visual Studio 15 Preview Installer Linki : http://aka.ms/vsnext

Visual Studio 15 Preview'ı kurdunuz ve hemen bir proje yaratıp özellikleri test etmek istiyorsunuz. Bir örnek yaptınız ama o da ne projeniz derlenmiyor ve hata veriyor.

Bu hatayı almamızın sebebi C# 7.0 özelliklerinin her birinin feature flagleri arkasına alınması. Yani ekip hızlı bir şekilde istediği özellikleri açıp kapatabiliyor gerekli olduğunda. Bu hatayı aşmanın yolu ise oldukça basit. Test yaptığımız projeye conditional compilation symbols olarak __DEMO__ ve __DEMO_EXPERIMENTAL__ 'ı eklemek.

Bu işlemi de yaptıktan sonra kodunuz başarılı olarak derlenecektir. Ancak her Visual Studio 15 Preview versiyonunda, duyurulan C# 7.0 özellikleri olmayabilir. Örneğin bu yazının yazıldığı dönemde bulunan Visual Studio 15 Preview içerisinde Tuple özellikleri henüz bulunmuyor.

Roslyn Source Code'unu Çalıştırıp Test Etmek

Gelelim diğer opsiyona. Github üzerinden Roslyn projesinin kodlarını indirip projeyi derleyip aynı şekilde özellikleri test edebilirsiniz. Daha önce bunu denemiş biri olarak uyarmam gerekir ki bu iş göründüğü kadar kolay değil. Kısaca yol göstermem gerekirse

Şimdiden bu opsiyonla uğraşacaklara kolaylıklar diliyorum :) Daha öncede bahsettiğim gibi en kolay opsiyon Visual Studio 15 Preview üzerinden testleri yapmak.

Bir sonraki yazıda görüşmek üzere.



C# 7.0'da Bizi Neler Bekliyor ?

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

C# 7.0'da Bizi Neler Bekliyor ?

C# 7.0 ile ilgili planlamalar daha C# 6.0 release olmadan önce başlasada bazı şeyler tam oturmadan herhangi bir paylaşımda bulunmak istememiştim. Artık Build konferansıyla beraber yavaş yavaş ana temaların belirginleşmesinden sonra C# 7.0 yazılarına başlamadan önce durumu özetleyen ve özelliklere kısaca değindiğim bir video çektim. Eğer C# 7.0 ile beraber neler gelecek diye merak ediyorsanız aşağıdaki videoyu izleyip bilgi sahibi olabilirsiniz.



Video Linki : https://www.youtube.com/watch?v=JPPWPtwXMFA



Visual Studio 2015 Update 2 İle Gelen Yenilikler

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

Build 2016'nın en önemli duyurularından biri de Visual Studio 2015 Update 2'nin release olmasıydı. Update 2 içerisinden bolca bug fix barındırsada bahsetmeye değer bazı yeni özellikler de barındırmakta.

Universal Platform Yenilikleri

Update 2 ile beraber Universal App'lerde kullandığımız Microsoft.NETCore.UniversalWindowsPlatform nuget paketininde 5.1.0 versiyonu release oldu. Visual Studio 2015 Update 2'yi yüklediğinizde bu nuget paketini update etmenizde fayda var.

Update 2 ile beraber gelen bir diğer özellik ise Universal App yaratma aşamasında karşımıza çıkan version seçim ekranı. Bu ekran ile uygulamanızın target edeceği ve minimum olarak destekleyeceği Windows 10 versiyonunu uygulama yaratırken seçebiliyorsunuz.

Update 2 ile beraber gelen en beğendiğim özellik ise basit ama zaman kazandıran bir özellik. Daha önceden Universal App'inizi store'a göndermek için paket yaratmak istediğinizde her seferinde live accountunuzla login olmak zorunda kalıyordunuz. Ancak Update 2 ile beraber bu sorunu çözmüşler ve Store'a bir kere login olduğunuzda Visual Studio artık bu account bilgilerini otomatik olarak hatırlıyor.

Interactive Window Yenilikleri

Visual Studio 2015 Update 1 ile beraber gelen en önemli yeniliklerden biri de Interactive Window'du. (Eğer daha önce Interactive Window'u duymadıysanız yazmış olduğum yazıyı okuyabilirsiniz.) Update 2 ile beraber Interactive Window tarafında da yenilikler mevcut. Bu yenilikler çalışmış olduğunuz proje içerisindeki kodları hızlı bir şekilde Interactive Window üzerinden çalıştırmanıza olanak sağlıyor. Bunun için Visual Studio içerisinde çalıştırmak istediğiniz kodu seçip sağ tıklayıp "Execute in Interactive"'i tıklamanız yeterli.

Editör Yenilikleri

Update 2 içerisinde güzel editör yenilikleri de bulunmakta. Bunlardan ilki using/import eklerken artık fuzzy matching yapılması. Yani eğer bir tipin adını yanlış yazarsanız Visual Studio tipin adından tahminde bulunuyor ve size uygun öneriyi sunup hem tipin adını düzeltiyor hem de ilgili using/import'u ekliyor.

Update 2 içerisinde ayrıca yeni refactoringler de bulunuyor. Bunlardan ilki delegate işletimlerinde null propagator operatörü kullanımını öneren refactoring.

Diğer refactoring ise make method synchronous refactoringi.

Bu yazıda değinmek istediğim yenilikler bunlar. Update 2 ile beraber gelen tüm yenilikleri https://www.visualstudio.com/en-us/news/vs2015-update2-vs.aspx adresinde bulabilirsiniz.



Visual Studio VNext - Visual Studio 15 Preview Yenilikleri

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

Bu seneki Build konferansının en önemli duyurularından biri de Visual Studio'nun bir sonraki major versiyonu olacak olan Visual Studio 15 Preview'dı. Daha ilk preview olmasına rağmen içerisinde güzel yenilikleri barındıran Visual Studio 15'e bu yazımızda kısaca bir gözatacağız.

Visual Studio 15 Preview adından da anlaşılacağı gibi henüz preview :). Dolayısıyla bilgisayarınıza yüklediğinizde eğer bir sorunla karşılaşırsanız bu sorunla ilgili Microsoft size destek vermeyecektir. Bu nedenle tavsiyem bilgisayarınızda yeni bir sanal makinada veya Azure üzerinde bir sanal makinada bu yenilikleri test etmeniz.

Yeni Installer

Bu yazıyı okuyorsanız tahmin ediyorum ki %90 ihtimalle hayatınızda en az bir kere makinanıza Visual Studio kurulumu yapmışsınızdır. Önce ufak bir setup dosyası download edilir sonra o setup dosyası arka planda 1-2 GB büyüklüğünde olan kurulum dosyalarını download eder ve sonrasında da 45dk-1 saate yakın Visual Studio'nun kurulmasını bekleriz. Kurulum zamanı setup sırasında nelerin kurulmasını istediğinize göre de değişir. Bazen developerlar yeniden kurulumla uğraşmamak için belki ileride lazım olur diye tüm opsiyonları seçerler. Bazen de default seçimle ilerlerler. Visual Studio ekibindeki kullanım datalarına bakarsak default seçenekle ilerleyen developerların %70'i default içerisindeki tüm functionalityleri ve tüm app tiplerini bile kullanmıyor.

Bu nedenlerden dolayı Visual Studio VNext'in en büyük temalarından biri bu kurulum aşamasını kolaylaştırmak ve hızlandırmak olacak. Hem setup dosyasının boyutunu ufaltma, hem kurulumu hızlandırma hem de Visual Studio'yu uygulama tipleri bazında modüler hale getirme şimdilik düşünülen yeniliklerden. Şimdi gelelim ilk preview'daki installer yeniliklerine.

http://aka.ms/vsnext adresine gidip sağdaki seçenekten yeni installerı download edip installer yeniliklerini test edebilirsiniz. Soldaki installer ise bildiğimiz klasik Visual Studio installerı.

Installerı download edip çalıştırdığımızda önceki versiyonların installerlarına nazaran farklı bir ekranla karşılaşıyoruz.

Ekrandan da görüleceği gibi artık Visual Studio çeşitli uygulama tiplerine uygun paketlerle geliyor. Yani eğer sadece .NET development yapacaksanız .NET Development paketini kurup hızlı bir şekilde kurulumu tamamlayabilirsiniz. Eğer sadece editör olarak kullanacağım diyorsanız core editörü seçip 300 MB'lık bir download yapıp Visual Studio'yu kurabiliyorsunuz. Şu anda sadece yukarıda resimde gördüğünüz kadar paket mevcut. Ancak zaman içerisinde Universal App paketi, Cloud App paketi gibi daha pek çok farklı paket installera eklenecektir.

Paket bazılı kurulumla beraber kurulum süreleri de gözle görülür bir şekilde düşüyor. Daha önce Visual Studio kurulumuyla 1-1.5 saatimizi harcarken artık çok daha kısa bir sürede kurulumu tamamlayabiliyoruz. Örneğin sadece core editör paketini yüklemek isterseniz max 10 dk içerisinde kurulum tamamlanıyor.(Paket downloadu yapıldığı için internet hızına göre değişiklik gösterebilir.)

Hızlandırılmış İlk Açılım

1-1.5 saat bekleyip Visual Studio kurulumunu tamamladıktan sonra ilk açtığımızda kullanacağımız dile göre seçim yaparız ve bir süre Visual Studio'nun ilk kullanıma hazırlanmasını bekleriz. Her ne kadar uyarıda bir kaç dakika sürebilir dese de bu süreç oldukça uzun sürebilmekte. Benim de şahsen Visual Studio kurulumunda en sinir olduğum şeylerden biridir bu :)

Visual Studio 15 ile beraber artık bu süre de oldukça kısalıyor ve 1 dakikayı bile bulmadan Visual Studio ilk kez bile olsa hızlıca açılıyor. Şahsen en sevdiğim özelliklerden biride bu Visual Studio 15 içerisindeki :D

Klasör Bazlı Çalışabilme

Yeni Visual Studio'nun en önemli özelliklerinden biri de klasör bazlı çalışabilme. Bundan önce Visual Studio'da bir projeyi açmak istediğimizde o projenin illa ki bir solution dosyası olması gerekiyordu. Ancak yeni Visual Studio ile beraber bu zorunluluk ortadan kalktı. Artık solution dosyası olmadan da doğrudan klasör bazlı olarak projeyi açıp çalışabiliyoruz. Böylece python, javascript gibi solution dosyası gerektirmeyen proje tipleriyle Visual Studio içerisinde çalışmak daha da kolay.

Solution Explorer içerisinde klasör görünümü

Code Style Rules

Roslyn projesiyle beraber custom analyzer yazabilmeye başlamamızla en çok sorulan sorulardan biri de proje içerisindeki code standartlarını korumak için bir analyzer yapabilmenin mümkün olup olmadıydı. Bununla ilgili 1-2 proje olsa da tam anlamıyla çözülemeyen bir sorundu bu. Artık Visual Studio 15'le beraber kodlama standartlarına uygun kurallar tanımlayabiliyoruz ve bu kuralların kontrolü projenin derleme aşamasında yapılıyor. Eğer kurala uygun olmayan kullanımlar varsa bunların fixleri için de Visual Studio size ilgili code fixi otomatik olarak sunuyor.

Code style kuralları şu anda Tools => Options ekranı içerisinde Text Editor => C# altında yer alıyor. Bu ekran içerisinde pek çok kural ayarlamak mümkün. Örneğin, projeler içerisinde this ve var keywordlerinin kullanımlarını belirleyebiliyorsunuz. Developerlar var keywordünü hiç kullanmamalı yada tipin belirgin olduğu durumlarda var keywordu kullanılabilir gibi kuralları ayarlayabiliyorsunuz.

Eğer isimlendirmelerle ilgili kurallar belirlemek isterseniz bu kuralları da yine aynı ekran üzerinden ayarlayabiliyorsunuz. Örneğin projede metot isimlendirmelerinde pascal casing kullanılsın diye ayarlarsanız Visual Studio derleme esnasında sizi isimlendirme kurallarına uymadığınız konusunda uyarıyor ve bunu düzeltmeniz için size code fix sunuyor.

Kuralları belirledikten sonra Visual Studio belirlediğimiz kurallara göre build esnasında ilgili uyarıları yapıyor.

"Show Potential Fixes"'a basarsak

Code style rule ekranı içerisinde yapılabilecek pek çok farklı ayar bulunmakta. Bununla ilgili daha detaylı bir şekilde başka bir blog yazısı yazmayı planlıyorum.

Diğer Özellikler

Build'deki sessionları izlerseniz Visual Studio 15 içerisinde yukarıda bahsettiklerime ek olarak gösterilen pek çok farklı özellik olduğunu göreceksiniz. Intellisense substring matching, intellisense filtering, "how do I" (en heyecanlanmadığım özellik :( ), code fix ile proje veya nuget paket referansı ekleme (en efsane özellik :) ) gibi özelliklerin hepsi private buildler üzerinden gösterildi. Bunun anlamı bu özellikle şu an için kullanıcılar tarafından test edilmek için hazır değiller. Tahmin ediyorum ki ilerleyen preview sürümlerinde bu özellikler de yavaş yavaş Visual Studio 15 içerisine eklenecektir.

Build'de Visual Studio 15 ile ilgili oturumlara aşağıda ulaşabilirsiniz.

Visual Studio 15 Preview yenilikleri şimdilik bu kadar. Yeni preview sürümleri çıktıkça içerisinde bulunan yeni özellikleri yine blogdan paylaşıyor olacağım.

Görüşmek üzere...



Roslyn - CSharpSyntaxWalker İle Syntax Tree Üzerinde Gezinme

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

Bir önceki roslyn makalemizde syntax tree yapısından ve syntax tree içerisinde bulunan yapılardan bahsetmiştik. Ayrıca syntax tree içerisinde istediğimiz yapıdaki elementleri bulmak için LINQ kullanmıştık. Bu yazıda ise syntax tree içerisinde gezinmenin farklı bir yolunu inceleyeceğiz. İlk olarak projemize yine Microsoft.CodeAnalysis nuget paketini ekliyoruz.

Install-Package Microsoft.CodeAnalysis 

Genelde syntax treeler ile çalışırken amacımız belirli bir tipte olan elementleri bulup bunlar üzerinde işlemler yapmak oluyor. Örneğin, tüm class tanımlamalarını bulup kontrol etmek veya değişken tanımlamalarını bulup kontrol etmek gibi... Bu nedenle aslında çoğunlukla ilk yaptığımız şey syntax tree içerisinde bulmayı amaçladığımız tanımlamalara gitmek. SyntaxWalker yapısı da otomatik olarak syntax tree üzerinde kolayca gezinmemizi ve istediğimiz tanımlamalara hızlıca ulaşmamızı sağlayan bir yapı. C# syntax tree'leri için bunun özelleşmiş hali de CSharpSyntaxWalker tipi. CSharpSyntaxWalker tipinin yapısına bakarsak bu tip abstract bir tip. Bu nedenle kendi SyntaxWalker tipimizi yazıp bu tipten türeteceğiz.

public class MethodWalker : CSharpSyntaxWalker
{

}

Diyelim ki çalıştığınız şirkette kullandığınız bir kodlama standardı var ve bu standarda göre yazdığınız metotların isimleri büyük harfle başlamalı. Bu nedenle syntax walker ile syntax tree içerisinde gezip metot tanımlamalarını bulup bu metotların isimlerini kontrol etmeliyiz.

Yukarıda gördüğünüz üzere CSharpSyntaxWalker içerisinde override edilebilecek çok sayıda metot var. Bu metotları override ettiğinizde syntax tree içerisinde metodun isminde yazan elementlere denk gelindiğinde override ettiğiniz metot çağırılıyor. Böylece sizde istediğiniz işlemleri burada yapabiliyorsunuz. En basitinden eğer tüm nodeları veya tokenları gezmek isterseniz Visit ve VisitToken metotlarını override edebilirsiniz. Böylece hızlı bir şekilde tree üzerinde istediğiniz elemanlara erişebilirsiniz.

public class MethodWalker : CSharpSyntaxWalker
{
    public override void VisitToken(SyntaxToken token)
    {
        base.VisitToken(token);
    }

    public override void Visit(SyntaxNode node)
    {
        base.Visit(node);
    }
}

Ancak daha spesifik tanımlamalara erişmekse amacınız daha özelleşmiş metotlar işinizi daha da kolaylaştırabilir. Bizim örneğinizde aslında biz sadece metot tanımlamalarına odaklanmak istiyoruz. Bu nedenle metot tanımlamalarına erişmek bizim için yeterli. Bu nedenle doğrudan VisitMethodDeclaration metodunu override ederek ilerliyoruz.

public class MethodWalker : CSharpSyntaxWalker
{
    public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
    {
        base.VisitMethodDeclaration(node);
    }
}

Şimdi sıra geldi ilgili kontrolü yapma aşamasına. VisitMethodDeclaration metodunu override ettiğimizde bize artık MethodDeclarationSyntax node'u parametre olarak geliyor. Artık yapmamız gereken metot adının ilk harfini kontrol edip bir eğer illegal bir kullanım varsa ismini collection içerisine eklemek.

public class MethodWalker : CSharpSyntaxWalker
{
    public List<string> illegalMethodNames = new List<string>();
    public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
    {
        if (char.IsLower(node.Identifier.ValueText, 0))
        {
            illegalMethodNames.Add(node.Identifier.ValueText);
        }
    }
}

Gördüğünüz gibi bu kadar basit. Şimdi sıra geldi bu yazdığımız syntax walker aracılığıyla kod üzerinde gezinmeye.

class Program
{
    static void Main(string[] args)
    {
        var tree = CSharpSyntaxTree.ParseText(@"class Foo
                {
                    void Bar()
                    {
                    }

                    void bar1()
                    {
                        //Illegal
                    }

                    void Bar2()
                    {
                    }

                    void m()
                    {
                        //Illegal
                    }

                    class InnerFoo
                    {
                        void innerM()
                        {
                            //Illegal
                        }
                    }
                }"
);

        MethodWalker methodWalker = new MethodWalker();
        methodWalker.Visit(tree.GetRoot());

        methodWalker.illegalMethodNames.ForEach(t => Console.WriteLine(t));
    }
}

Gördüğünüz gibi syntax tree yarattıktan sonra syntax walkerın visit metodunu syntax tree'nin rootunu göndererek çağırıyoruz ve ağaç üzerinde gezinmeye başlıyoruz. Uygulamayı çalıştırırsak çıktı olarak küçük harfle başlayan metotların isimlerinin ekrana yazıldığını görebiliriz.

Gördüğünüz üzere syntax tree üzerinde gezinmek roslyn API'ları sayesinde oldukça kolay. Siz de kod içerisinde belirli analizleri yapmak için hızlı bir şekilde ve oldukça az kod yazıp ilgili kontrolleri yapabilirsiniz. Peki analizleri yaptık ve hataları kısımları bulduk. Bunları nasıl temizleyeceğiz ? Örneğin küçük harfle başlayan metotların isimlerini otomatik olarak değiştirsek nasıl olur ? Bu da bir sonraki roslyn makalesinin konusu olacak :)

Bir sonraki makalede görüşmek üzere...