Selam Arkadaşlar!
Gencay Yıldız'ın YouTube'da ücretsiz olarak yayımladığı C# ve OOP derslerini izledim ve bu konuları öğrenmek isteyenlere bu serileri ayrıca ücretli içerik kısmındaki ücretli OOP, C# ileri düzey 1 ve 2, Creational Design Pattern adlı serilerini de tavsiye ederim.
Ücretsiz yayımlanan kısımda eğitmen kimi zaman "bunu mülakatlarda sorabilirler" diye ekleme yaptığı kısımlar oluyor. Bu anları derlediğim oynatma listesine buradan ulaşabilirsiniz.
Bu anlarda paylaşılan kodları ve bilgileri de burada sizlerle paylaşacağım ancak önceden belirtmek isterim ki bu paylaşacaklarımın bu derslerin önemli kısımları, özet kısımları, bilmezseniz bilmezsiniz kısımlardan daha çok detay bilgilerdir.
Kimi bilgileri bilmeden de çok rahat kod yazabilirsiniz ki okullu ve iki yıldır sektörde çalışan arkadaşım dahi bu bilgileri bilmediğini ve önemli olmadıklarını iletmişti. Kimi bilgileri ise zaten normal işleriniz sürecinde hali hazırda kullandığınız, bildiğiniz bilgilerdir.
Yeni öğrenecekler için bu paylaşacaklarımı değil de yukarıda linkini verdiğim video serisini izlemelerini tavsiye ederim. Ancak ilk kez izleyecekler bilmeliler ki Gencay Yıldız OOP 27. video dk45.'de abstract metotların sadece public olabilceğinden bahsediyor ancak c sharp'da sadece private olamıyorlar. Onun dışında protected, protected internal, internal ve public olabiliyor. Ayrıca C# dersleri 401. videosunun 11:20.sn'sindeki kodun doğrusu için 47. satır "ref int b = ref X(ref a);" olarak değiştirilmeli.
Şimdi sorular, bilgiler ve kodlarla beraber Gencay Yıldız'ın "mülakatta sorabilirler" dediklerini paylaşacağım
1. Değişken/Field'ların default değişken durumu ve Eğer sayı int max değerinden küçükse default olarak int, ondalıklı bir sayı ise double türü atanır.
2.Static, ReadOnly ve Const arasında karşılaştırılmalı bilgi.
3.Değer türlü değişkenlerde Shallow Copy, referans tipli değişkenlerde Deep Copy yapma
4.sayısal değerler arası tür değişimi:
5.Object, Var, Dynamic, Nullable hakkında bilgi.
6.Aritmatik operatörlerde geri dönüş değeri (Sefer Algan'ın, BTK Akademi'de eğitmenlik yapacakların yetkinliğini test eden kişi, zamanında sorduğu bir bilgi)
7.Pattern Matching.
Mülakat sorusu dediği bilgi: Var ile Var Pattern arasındaki fark nedir?
8.Switch, if, switch expression /+ Patterns. Switch ile If arasındaki fark neydi?
9.Keyword nedir, operatörden farkı nedir? (Teorik)
10 İIçi boş döngüler için scopesuz syntax.
11. Düzensiz diziler/Dizi içinde diziler nedir?
12. Metotlarda "in" keywordü.
13. Local Func. ve Static Local Func.
14. Out Keyword'ü ve TryParse'da out kullanmak.
15. Nested Type: Sınıf içinde sınıf oluşturmaktır. İçeride oluşturulan bu sınıf üsteki sınfın member'ı değildir.
16. Innit-Only Propertys ile Getter-Only Propertys arasında ne fark var?
17. Recordlar ve sınıfların karşılaştırılması
18. Positional Record Nedir?
19. Record-Init ile kolayca property'si değiştirilimiş klonlar üretme.
20. İnit kullandığımız senaryoda karşılaştırmalı diğer yöntemlerle Property'leriştirilmiş objectler elde etme - Class-init, Class-init with Function, Record-init with expresion
21. Static Constructor: Bir nesne oluşturulurken ilk tetiklenen fonksiyon nedir? (Teorik)
İlgili sınıftan ilk defa nesne üretiliyorsa ilk tetiklenecek fonksiyon static constructor'dır.
N. defa nesne üretiliyorsa ilk tetiklenecek fonksiyon constructor'dır.
İlla o sınıftan ilk defa bir nesne üretiliyor olmasına gerek yok, ilgili sınıf içeresinde herhangi bir static yapılanmanın
tetiklenmesi ile de static constructor tetiklenebilir. Static constructor bir kere tetiklendikten sonra bir daha tetiklenmez.
22. Kalıtım-NameHiding-Virtual/Override ve Polymorphism ve onda bilinçli tür dönüşümleri.
23. :base(), :this()
24. Compile'da değil ancak run time hatası verecek trick bir kod.
25. is-a/has-a/can do.
26.Mülakatta Çıkmış Sealed*
27. Partial.
28. Teroik: Abstract Class - Interface.
29. Interfaceler.
30. Explicity Implement
31. Class ile Interface arasında "NameHiding", Derived Class hem Base class'dan hem de interface'den aynı isimli metot alıyorsa ne olur?
32. Default Implementation
Ekstra bilgi: Delegate'ler object sınıfından türemez, Record'lar class'lardan kalıtım alamaz, record'lardan kalıtım alırlar, Statik Polimorfizm: Klasik metot overloading'de nesne referansına verdiğimiz parametre ile hangi overload metottun çalışacağı durumudur.
Dinamik Polimorfizm: Hangi metottun tetikleneceğinin run time'da belirlenmesi.
24. Compile'da değil ancak run time hatası verecek trick bir kod'u daha iyi anlamanız için bir görsel;
İyi çalışmalar!
Gencay Yıldız'ın YouTube'da ücretsiz olarak yayımladığı C# ve OOP derslerini izledim ve bu konuları öğrenmek isteyenlere bu serileri ayrıca ücretli içerik kısmındaki ücretli OOP, C# ileri düzey 1 ve 2, Creational Design Pattern adlı serilerini de tavsiye ederim.
Ücretsiz yayımlanan kısımda eğitmen kimi zaman "bunu mülakatlarda sorabilirler" diye ekleme yaptığı kısımlar oluyor. Bu anları derlediğim oynatma listesine buradan ulaşabilirsiniz.
Bu anlarda paylaşılan kodları ve bilgileri de burada sizlerle paylaşacağım ancak önceden belirtmek isterim ki bu paylaşacaklarımın bu derslerin önemli kısımları, özet kısımları, bilmezseniz bilmezsiniz kısımlardan daha çok detay bilgilerdir.
Kimi bilgileri bilmeden de çok rahat kod yazabilirsiniz ki okullu ve iki yıldır sektörde çalışan arkadaşım dahi bu bilgileri bilmediğini ve önemli olmadıklarını iletmişti. Kimi bilgileri ise zaten normal işleriniz sürecinde hali hazırda kullandığınız, bildiğiniz bilgilerdir.
Yeni öğrenecekler için bu paylaşacaklarımı değil de yukarıda linkini verdiğim video serisini izlemelerini tavsiye ederim. Ancak ilk kez izleyecekler bilmeliler ki Gencay Yıldız OOP 27. video dk45.'de abstract metotların sadece public olabilceğinden bahsediyor ancak c sharp'da sadece private olamıyorlar. Onun dışında protected, protected internal, internal ve public olabiliyor. Ayrıca C# dersleri 401. videosunun 11:20.sn'sindeki kodun doğrusu için 47. satır "ref int b = ref X(ref a);" olarak değiştirilmeli.
Şimdi sorular, bilgiler ve kodlarla beraber Gencay Yıldız'ın "mülakatta sorabilirler" dediklerini paylaşacağım
1. Değişken/Field'ların default değişken durumu ve Eğer sayı int max değerinden küçükse default olarak int, ondalıklı bir sayı ise double türü atanır.
C#:
class Program
{
static void Main(string[] args)
{
Deneme m = new Deneme();
m.Methot();
Console.WriteLine(1.GetType()); //Tam sayı, default olarak int
Console.WriteLine($"int max değer: {int.MaxValue}. " +
$"Bu değerden büyük sayılar ise default olarak:{2_147_483_648.GetType()}");
Console.WriteLine(3.14.GetType()); //Ondalıklı sayı, default olarak double
}
}
class Deneme
{
//Sınıflarda tanımlanan değişkenlere/Field'lara default değer atanır
int a;
public void Methot()
{
//Metotlarda tanımlanan değişkenlere default değer atanmaz
int b;
//Console.WriteLine(b); Bu kod hata verir.
Console.WriteLine(a);
}
2.Static, ReadOnly ve Const arasında karşılaştırılmalı bilgi.
C#:
// Static: Stack, Heap gibi Static de bellekte bir alandır.Static ile uygulama bazında, nesneye üzerinden erişmeye ihtiyaç duymadan
// erişilebilen veri depolayabiliriz.
//Const'lar özünde static'dir ancak aralarındaki fark static değişkenlerin değeri değişebilirken constlar tanımlandığı anda değer ataması
//yapılmalıdır.Verilen değer sabittir, değiştirilemez.
//ReadOnly: Sadece okunabilir değişkenler tanımlamak için kullanılır.Const'lar da bir fakı sadece tanımlandığı yerde değil ayrıca constructor
//içinde de değeri atanabilir. Dependency Injection deseninde çok sık tercih edilir. ReadOnly Static değildir.
//Ekstra Bilgi:Attribute'u kullanan yapı üzerinden MyAttribute'un içindeki constructor'a veya Propertylere değer verilecekse bu const türünde
//olmalıdır, static türde olamaz.
class ReadOnlyConstFarkınaOrnek
{
const int ConstVariable = 0;
readonly int ReadOnlyVariable;
public ReadOnlyConstFarkınaOrnek()
{
ReadOnlyVariable = 5;
}
}
[AttributeUsage(AttributeTargets.All)]
class MyAttribute : Attribute
{
public MyAttribute(int i)
{
}
public int MyProperty { get; set; }
public int MyProperty1 { get; set; }
}
[My(i, MyProperty = i, MyProperty1 = i)]
class MyClass
{
private const int i = 5;
//static int i = 5; //Static Hata verir, const olmalı.
//int i = 5; //Bu da hata verir, değişken olmamalı.
}
3.Değer türlü değişkenlerde Shallow Copy, referans tipli değişkenlerde Deep Copy yapma
C#:
class Program
{
static void Main(string[] args)
{
//Değer türlü değişkenlerde deep copy
int __a = 5;
int __b = __a;
Console.WriteLine($"__a: {__a}, __b: {__b}");
__a = 21;
Console.WriteLine($"__a: {__a}, __b: {__b}");
//Değer türlü değişkenlerde shallow copy
int a = 5;
ref int b = ref a;
Console.WriteLine($"a: {a}, b: {b}");
a = 15;
Console.WriteLine($"a: {a}, b: {b}");
}
}
C#:
class Program
{
static void Main(string[] args)
{
/*Referans türlü değişkenlerde varsayılan olan shallow copy'e örnek
MyClass m1 = new();
MyClass m2 = m1;
*/
//referans türlü değişkenlerde deep copy örnek
MyClass _a = new MyClass();
MyClass _b = _a.Copy();
}
class MyClass
{
public MyClass Copy()
{
return (MyClass)this.MemberwiseClone();
}
}
}
4.sayısal değerler arası tür değişimi:
Bir sayısal değeri kendi türünden daha büyük değer aralığına sahip diğer türlere dönüştürülmesi bilinçsiz, daha küçük değer aralığına sahip diğer türlere dönüştürülmesinde ise hedef türün ilgili veriyi karşılayamam riskinden dolayı buradaki işlemi bilinçli yapmamız gerektiğinden bu türe bilinçli tür değişimi denilir. Eğer dönüştürülen tür gönderilen değeri kapsamıyorsa ilgili değer hedef türe göre modu alınarak kalan değer yazılacaktır ve bundan dolayı veri kaybı yaşanır.
a, int'dan byte bilinçli tür dönüşümü yaşar ve bu örnektebyte'ın veri aralığı 0-256 olduğu için veri kaybı da yaşanır. Ardından byte'dan short'a bilinçsiz tür dönüşümü gerçekleşir
C#:
int a = 3000;
short s = (byte)a;
Console.WriteLine(s);
5.Object, Var, Dynamic, Nullable hakkında bilgi.
C#:
var: Conpiler aşamasında, Verilen türe göre kendi türünü belirleyen bir keyworld'dür, değişken türü değildir. Değer, tanımlanma
aşamasında verilmelidir. İlk değer verildikten sonra değeri ilerleyen noktada değiştirilemez. Var ile object arasındaki fark;
var a = 5, stack'da int olarak tutulurken, object y = 5; heap'de içersinde int barındıran bir obje tutar.
Var'la tanımlanan bir değere direk ulaşıp kendi öz türünde işlemler yapılabilirken object'de önce unbox edilmelidir.
Dynamic: Varın runtime'daki versiyonudur. Geliştirme aşamasında verilen türe dönüşmediği için atanan türe direk ulaşıp öz
türündeki işlemleri (mesela metotlarına, property'lrtine erişim) yapılamaz ve atanan tür geliştirme aşamasında değiştirilebilir
*/
var _x_ = 3;
_x_.ToString();
dynamic y = 3;
// y. <= ?
Console.WriteLine(y.GetType()); //Burada int
y = "hello";
Console.WriteLine(y.GetType()); //Burada string
/* nullable: C#^da normalde değer türlü değişkenlere null değer atanamaz. Bir değer türlü değişken nullable yani "?" işareti
* ile null olabilir yapabilir ve ardından as, is null gibi operatörleri kullanabiliriz ancak şu bilinmeli;*/
object x = 123;
int? __y = x as int?; // burada x'i int olarak çıkarabilmek hem x nullable formatta hem de onu atadığımız y nullable formatta olmalı
6.Aritmatik operatörlerde geri dönüş değeri (Sefer Algan'ın, BTK Akademi'de eğitmenlik yapacakların yetkinliğini test eden kişi, zamanında sorduğu bir bilgi)
C#:
//Aynı türde işlem yapılırken geri dönüş değeri/sonuç aynı tür olur;
int a = 5; int b = 10; int c = a * b;
//Farklı türde işlem yapılırken geri dönüş değeri/sonuç büyük olan/veri aralığı daha geniş olan türde olur;
int s1 = 10; double s2 = 5; double s = s1 + s2;
//İstisna: byte ve byte, ushort ve ushort, short ve short arasında bir işlem yapılıyor ise geri dönüş değeri/sonuç int olur:
byte _b1 = 10;
byte _b2 = 5;
int sonuç = _b1 + _b2;
7.Pattern Matching.
C#:
//Type Patternn : Object içersindeki bir tipin belirlenmesininde kullanılan is operatörünün desenleştirilmiş halidir
object x = 125;
if (x is string y)
Console.WriteLine("X bir string'di ve değeri y değişkenine atandı");
else if (x is int z)
Console.WriteLine("X bir int'dı ve değeri z değişkenine atandı");
/*
if scope'u dışında tppe pattern ile oluşturduğum değişkene erişemem yani şu kod hata verir;
Console.WriteLine(y); Çünkü x bir string değilse null olacaktır. Ancak onun if scope'u içinde erişilebilir
Çünkü tabii ki de eğer if true ise null değil, bahsedilen türdür ve atama yapılmıştır.
Type Pattern olmasaydı şöyle yazacaktık;
if (x is string)
{
string _y = x as string;
Console.WriteLine("X bir string'di ve değeri _y değişkenine atandı");
}*/
C#:
//Constant Pattern: Elimizdeki değeri sabit bir değer ile karşılaştırmamız sağlar.
object technopat = "technopat";
if (technopat is "sosyal") //Değersel bazda kontrol
Console.WriteLine("Lorem");
else if (technopat is 5) //Değersel bazda kontrol
Console.WriteLine("Ipsum");
else if (technopat is string) //Tür bazlı kontrol. Bu bir Constant Pattern değildir, bu kontrol
//is operatörünün normal işlevidir. Bunu C# 7.0'dan önce de yapabiliyorduk.
//Constant Pattern özelliği sonradan eklendi.
Console.WriteLine();
Mülakat sorusu dediği bilgi: Var ile Var Pattern arasındaki fark nedir?
C#:
object sosyal = "sosyal";
if (sosyal is var t) //Eldeki değişkeni var ile elde etmemizi sağlar
Console.WriteLine($"Tür ne olursa olsun var ile ona börünecek ve t değişkenine atacak. " +
"Hızlandırılmış bir Type Pattern gibi düşünülebilir");
//Varr Pattern: Normal var derleyici anında ilgili türe dönüşürken var pattern'daki var
//runtime'da ilgili türe dönüşür. Var, var pattern'da sanki dynamic gibi çalışsa da "dynamic pattern" diye
//bir şey yoktur. Dolayısyla bu örnekte var'ı silip dynamic yazamayız.
8.Switch, if, switch expression /+ Patterns. Switch ile If arasındaki fark neydi?
C#:
* Önceden switch'de sadece eşitlik durumu kontrol edilebilir, if'le tüm durumlar kontrol edilebilirken C# 9 ile gelen
* relational pattern ile <,>, <=, >= gibi durumlar ve logical pattern ile and, or, not gibi mantıksal durumlar switch'de de ,
* kontrol edilebilir oldu.
*/
int number = 60;
string result = number switch
{
> 10 and < 50 => "10'dan büyük, 50'den küçük",
> 50 or < 100 and 60 => "50 büyük veya 100'den küçük ve 60'a eşit",
not 51 => "51 değil"
};
9.Keyword nedir, operatörden farkı nedir? (Teorik)
Keyworld nedir? : Programlama dilinin en atomik parçalarındandır diyebiliriz.
Derleyici için ön tanımı olan, nerede hangi amaca hizmet edeceği belli ve kod içeresinde hangi noktalarda
çağrılabileceği/kullanılabileceği kurallarla sınırlandırılmış olan anahtar sözcüklerdir.
Operatörlerden ne farkı vardır? Operatörler esas olarak belli bir operasyonu yani eylemi üstlenen yapılardır. Halbuki
keyworlder daha geniş kavramlardır. Bazen bir keyworld bir operatörü temsil edebilir. Mesela "is" ya da "TypeOf" keyworld'ünde
olduğu gibi.TypeOf bir keyworld'dür ama operatör işlevi yapan bir keywordl'dür.
Direk eyleme, bir fiiliyata odaklanmş bir yapılanma varsa bu operatördür ama yoksa/daha farklı amaçlara hizmet ediyorlarsa
bunlar keyworld'dü
Operatörler keyworld'dür ancak her keyworld bir operatör değildir, tıpkı "static" veya "class"da olduğu gibi.
Derleyici için ön tanımı olan, nerede hangi amaca hizmet edeceği belli ve kod içeresinde hangi noktalarda
çağrılabileceği/kullanılabileceği kurallarla sınırlandırılmış olan anahtar sözcüklerdir.
Operatörlerden ne farkı vardır? Operatörler esas olarak belli bir operasyonu yani eylemi üstlenen yapılardır. Halbuki
keyworlder daha geniş kavramlardır. Bazen bir keyworld bir operatörü temsil edebilir. Mesela "is" ya da "TypeOf" keyworld'ünde
olduğu gibi.TypeOf bir keyworld'dür ama operatör işlevi yapan bir keywordl'dür.
Direk eyleme, bir fiiliyata odaklanmş bir yapılanma varsa bu operatördür ama yoksa/daha farklı amaçlara hizmet ediyorlarsa
bunlar keyworld'dü
Operatörler keyworld'dür ancak her keyworld bir operatör değildir, tıpkı "static" veya "class"da olduğu gibi.
10 İIçi boş döngüler için scopesuz syntax.
C#:
//Asenkron işlemlerde içi boş sonsuz döngülere ihtiyacımız olabiliyor. O durumlarda bu syntax de doğrudur
while (true) ;
for (; ; );
do;
while (true);
if (true); //Bu sonsuz döngü değil ve böyle bir yapıya ne zaman ihtiyaç olunur bilmiyorum ama neticede bu sytax da doğrudur.
11. Düzensiz diziler/Dizi içinde diziler nedir?
C#:
//İçerisinde belirtilen türde ve adette, farklı boyutlarda diziler tutan dizilerdir.
int[][] numbers = new int[3][]; //her biri içerisinde kendi int dizisini barındıran 3 tane diziyi tutan dizi
numbers[0] = new int[5] { 3, 5, 7, 9, 11 };
numbers[1] = new int[3] { 1, 2, 3 };
numbers[2] = new int[2] { 14, 15 };
Console.WriteLine(numbers.Length); //3
Console.WriteLine($"{numbers[0].Length + numbers[1].Length + numbers[2].Length} \n"); //10
for (int i = 0; i < numbers.Length; i++)
{
for (int j = 0; j < numbers[i].Length; j++)
{
Console.Write($"{numbers[i][j]}\t");
}
Console.WriteLine("\n");
}
12. Metotlarda "in" keywordü.
C#:
//parametreleri ReadOnly hale getiri,metot içinde değiştirilmesini engeller.
public void X(in int a, int b, in char c)
{
b = 123; //Bunda sorun yok
a = 15; //X ! Hata verecektir
c = "t" //X ! Hata verecektir
}
13. Local Func. ve Static Local Func.
C#:
/*Local Funct yokken eskiden muadili olarak Anonim, Delegates, Func gibi yapılar kullanılıyordu
* Metotları; Class'ların, Record'ların, Interface'lerin, Abstract Class'ların, Struct'ların ve "Local Functions" özelliği
* ile başka metotların içinde metot tanımlaması yapılabilir. Local Func.'larda access modifiers yazılamaz . Herhangi bir şey
* yazılmadığı için de dolaylı olarak private olurlar.
Local. Func.'ların mantıksal ismi bulunduğu metot'un ismi ile aynı olamamalıdır. Olması durumunda hata vermez ama recursive
function kullanılmak istendiğinde karışıklık yaratır. Local fonct'lar sadece tanımlandıkları metotta ve o metotun içinde
herhangi bir yerde kullanılabilirler.
Local Funct. bulunduğu metottun içindeki değişkenlere veya bulunduğu metottun parametresin doğrudan ulaşması maliyetli olduğu
için static local funct. kullanmak daha doğrudur.
*/
public void X(int a)
{
int b = 0;
void Y()
{
Console.WriteLine(a);
Console.WriteLine(b);
}
}
// Local fonksiyonun bir üst fonsksiyonun parametresi ve içinde tanımlanmış değişkenlere erişebildiğini göstermeye yarıyor
// ancak bu işlem çok maliyetli olduğu ve bu özelliği kapatmak için static local fonksiyon kullanarak alınacak değeri parametre
// üzerinden veriyoruz
public void X(int a)
{
int b = 0;
void Y(int a, int b)
{
Console.WriteLine(a);
Console.WriteLine(b);
}
Y(3, 5);
}
//Local fonksiyon ile üst metot isimleri aynı olmamalı. Hata vermez ama recursive function'larda kafa karışıklığı oluşturur.
static void Main(string[] args)
{
X();
}
static public void X()
{
X();
void X()
{
Console.WriteLine("İç");
}
Console.WriteLine("Dış");
}
//---------------------------------------------------
static void Main(string[] args)
{
Y();
}
static public void Y()
{
void X()
{
Console.WriteLine("İç");
}
Console.WriteLine("Dış");
Y();
}
14. Out Keyword'ü ve TryParse'da out kullanmak.
C#:
//out keyword'ü ile metotların parametrelerinden dışarıya değer döndürebilriz (return gibi düşünülebilir).
static void Main(string[] args)
{
/*string stringSample = "";
int intSample = 0; */
//İster bu yukarıdaki gibi tanımlar, içine boş değer atarız istersek alta yaptığım gibi değişkeni, mantıksal
//adını yazarak, parametre içinde tanımlarız
Y(out string stringSample, out int intSample, 123);
}
static public void Y(out string a, out int b, int c)
{
a = "Out String";
b = 25;
}
#region TryParse'da out kullanmak
string a = "123";
if (int.TryParse(a, out int r))
{
//Eğer dışarıdan türünü bilmediğimiz bir değişken gelecek ve buna tür dönüşümü uygulamak istersek ve hedef türe
//dönüştürülemiyorsa (Bu örnekte olduğu gibi) RunTime hatası yemektense TryParse ile ilgili değişken hedef türe
//dönüştürülmeye uygunsa dönüştür ve verilen yeni değişkene ata + duruma göre bool dön işlemini yapabiliriz.
}
else {/*Eğer dışardıan gelen şeyin türü istediğimize döndürülemiyorsa yukarısı False dönecek ve else fonksiyonu olarak
* burada gerekli işlemleri yapacağız */}
15. Nested Type: Sınıf içinde sınıf oluşturmaktır. İçeride oluşturulan bu sınıf üsteki sınfın member'ı değildir.
16. Innit-Only Propertys ile Getter-Only Propertys arasında ne fark var?
C#:
static void Main(string[] args)
{
Book book = new Book() { Author = "lorem", Name = "Ipsum" }
;
Console.WriteLine(book.Author);
Console.WriteLine(book.Name);
}
class Book
{
public string Author { get; init; } = "hello";
public string Name { get; init; }
public Book()
{
Name = "world";
}
}
C#:
class Program
{
static void Main(string[] args)
{
Book book = new Book()
{
Name = "ReadOnly Field'ların Init Only Property ile kullanımı"
};
Console.WriteLine(book.Name);
}
}
class Book
{
private readonly string name;
public string Name
{
get { return name; }
init { name = value; }
}
}
17. Recordlar ve sınıfların karşılaştırılması
C#:
//Record'lar özünde class'lardır, değer türlü yapılar değildir. Recordlar referans türlü yapılardır.
//Bir objenin topyekûn olarak sabit/değişmez olarak kalmasını sağlar. Nesnedense nesnenin verilerini/değerleri ön planda tutar.
static void Main(string[] args)
{
____MyClass m1 = new ____MyClass() { ___MyProperty = 5 };
____MyClass m2 = new ____MyClass() { ___MyProperty = 5 };
MyRecord m3 = new MyRecord() { MyProperty = 5 };
MyRecord m4 = new MyRecord() { MyProperty = 5 };
Console.WriteLine($"iki farklı instance olduğu için {m1.Equals(m2)}");
Console.WriteLine($"iki farklı instance olsa da dataları/değerleri aynı olduğu için {m3.Equals(m4)}");
}
class ____MyClass
{
public int ___MyProperty { get; set; }
}
record MyRecord
{
public int MyProperty { get; init; } //Bu kullanıma Norminal Records denilir
}
//Redord'Un amacı değiştirilemez değerler yapıp değeri ön planda tutmak. Ancak istenilirse init yerine set kullanılarak
//değiştilebilir de yapılabilir
18. Positional Record Nedir?
C#:
//Bir record üzerinde, parametre aracılığı ile, Contractor ve Deconstruct yapılanmasını hızlı bir şekilde
//oluşturmamamızı sağlayan bir semantikdir
static void Main(string[] args)
{
NonPositionalRecord npr = new("abc", "def");
var (x, y) = npr;
Console.WriteLine(npr.Name);
PositionalRecord pr = new("positionalName", "positionalSurname");
var (_x, _y) = pr;
Console.WriteLine(pr.positionalName);
PositionalRecord prWithCustomCtor = new(); //The value in the costum ctor's ": this" will be used
var (_x_, _y_) = prWithCustomCtor;
Console.WriteLine(prWithCustomCtor.positionalName);
}
record NonPositionalRecord
{
public string Name { get; init; }
public string Surname { get; init; }
public NonPositionalRecord(string name, string surname)
{
Name = name;
Surname = surname;
}
public void Deconstruct(out string name, out string surname)
{
name = Name;
surname = Surname;
}
}
//Rather than doin' it like these we can use Positional Records instead
record PositionalRecord(string positionalName, string positionalSurname)
{
//That's it. Positional Record creates a propertys in the init format given in the parameter for us and create
//the constractor and deconstruct accordingly.
//Note: While using Positional Records if we ever wanted to create our costum constractor we MUST use the P.R's one
public PositionalRecord() : this("a", "b")
{
}
}
19. Record-Init ile kolayca property'si değiştirilimiş klonlar üretme.
C#:
static void Main(string[] args)
{
Employee employee = new Employee()
{
Name = "lorem",
Surname = "ipsum",
Position = 1
};
Employee employee1 = employee with { Position = 2 };
Employee employee2 = employee with { Name = "something else", Position = 3 };
Employee employee3 = employee with { Name = "test", Surname = "new surname", Position = 4 };
}
record Employee
{
public string Name { get; init; }
public string Surname { get; init; }
public int? Position { get; init; }
}
20. İnit kullandığımız senaryoda karşılaştırmalı diğer yöntemlerle Property'leriştirilmiş objectler elde etme - Class-init, Class-init with Function, Record-init with expresion
C#:
class Program
{
static void Main(string[] args)
{
MyClass lorem = new()
{
MyProperty = 1,
MyProperty1 = 2,
};
//If we ever need another object with "MyProperty2" = 5,
//we should do the following in the "class-init" version
MyClass lorem1 = new()
{
MyProperty = lorem.MyProperty,
MyProperty1 = 5,
};
//As you can see, we had to create a new object and also it took alot of time to code.
//While we are using Class-init with func. version if we ever need another object with
//"MyProperty2" = 5, we should do the following
MyClass2 ipsum = new()
{
MyProperty = 1,
MyProperty1 = 2
};
MyClass2 ipsum2 = ipsum.With(5);
//Now we didn't have to create a new object, but there is a better method.
//While using Record-init combo if we ever need another object with
//"MyProperty2" = 5, we should do the following
MyRecord dolor = new()
{
MyProperty = 1,
MyProperty1 = 2
};
MyRecord dolor1 = dolor with { MyProperty1 = 5 };
}
}
//Class-init version
class MyClass
{
public int MyProperty { get; init; }
public int MyProperty1 { get; init; }
}
//Class-init with func. version
class MyClass2
{
public int MyProperty { get; init; }
public int MyProperty1 { get; init; }
public MyClass2 With(int property1)
{
return new MyClass2
{
MyProperty = this.MyProperty,
MyProperty1 = property1
};
}
}
//Record-init
record MyRecord
{
public int? MyProperty { get; init; }
public int? MyProperty1 { get; init; }
}
21. Static Constructor: Bir nesne oluşturulurken ilk tetiklenen fonksiyon nedir? (Teorik)
İlgili sınıftan ilk defa nesne üretiliyorsa ilk tetiklenecek fonksiyon static constructor'dır.
N. defa nesne üretiliyorsa ilk tetiklenecek fonksiyon constructor'dır.
İlla o sınıftan ilk defa bir nesne üretiliyor olmasına gerek yok, ilgili sınıf içeresinde herhangi bir static yapılanmanın
tetiklenmesi ile de static constructor tetiklenebilir. Static constructor bir kere tetiklendikten sonra bir daha tetiklenmez.
22. Kalıtım-NameHiding-Virtual/Override ve Polymorphism ve onda bilinçli tür dönüşümleri.
Kalıtımsal durumda nesne üretim hiyerarşisi vardır. İlgili sınıftan bir nesne üretilirken önce onun base class'ı tetiklenir (ve onun base class'ı diye gider). En tepede de "object" class'ı vardır. Hiç kalıtım almadan bir sınıftan nesne oluşturuyoruz gibi gözükse de aslında o da object sınıfından kalıtım alır ki içi boş bir sınıftan nesne oluştursak bile objecten aldığı memberlara oluşturduğumuz nesnenin referansında "Member Access" operatörü (yani nokta[.]) kullanarak ulaşabiliriz. O gördüğümüz memberlar Object class'ından gelir.
Name Hidding: Kalıtım durumunda atalardaki herhangi bir member'ın mantıksal adı (yani bizim verdiğimiz ismi) ile alt nesildeki herhangi bir class'ın member'ının adı aynı olabilir. Bu durumda eskiden bu davranış bilinçli yapılması gerekir ve alt nesildeki sınıftaki aynı isimli member'a "new" takısı eklenirdi. Eskiden bu olmadan hata verirken şimdi sadece warnings (uyarı) veriyor.
Name Hiding ile virtual arasındaki fark: Sanal yapılar: Bir sınıf içerisinde bildirilmiş olan ve o sınıftan türeyen alt sınıflarda tekrar bildirilebilen/yeniden oluşturulabilen/var olan işlevselliği değiştirilebilen ve alt nesli de yapılan değişikliğe göre etkileyen yapılardır. NameHidingde olduğu gibi bir isim çakışmasından öte tanımlana yapının ezilip yeniden tanımlanma durumu vardır.
Name Hidding: Kalıtım durumunda atalardaki herhangi bir member'ın mantıksal adı (yani bizim verdiğimiz ismi) ile alt nesildeki herhangi bir class'ın member'ının adı aynı olabilir. Bu durumda eskiden bu davranış bilinçli yapılması gerekir ve alt nesildeki sınıftaki aynı isimli member'a "new" takısı eklenirdi. Eskiden bu olmadan hata verirken şimdi sadece warnings (uyarı) veriyor.
Name Hiding ile virtual arasındaki fark: Sanal yapılar: Bir sınıf içerisinde bildirilmiş olan ve o sınıftan türeyen alt sınıflarda tekrar bildirilebilen/yeniden oluşturulabilen/var olan işlevselliği değiştirilebilen ve alt nesli de yapılan değişikliğe göre etkileyen yapılardır. NameHidingde olduğu gibi bir isim çakışmasından öte tanımlana yapının ezilip yeniden tanımlanma durumu vardır.
C#:
class X { public void x() { } }
class Y : X { public void y() { } }
class Z : Y { public void z() { } }
class Program
{
static void Main(string[] args)
{
//Polymorphism daha iyi anlamak için bir örnek
X x = new Z();
Y y = new Z();
Z z = new Z();
z.z(); z.y(); z.x(); //x, y, z tüm metotlar var
y.y(); y.x(); //Her ne kadar Z objesini refere etsek de bunu yapmak için Y'yi kullandığımız için Z nesnesi üzerindeki
//(kalıtımla geçmiş olan) Y'ye ait özellikleri getiriyor. Dolayısıyla sadece x ve y var, kalıtımsal olarak Y sınıfının
//altında bulunan Z'nin memberları yani z metotu yok.
x.x(); //X'i kullanarak Z objesini refere ettiğimiz için sadece x metotu var.
// Polymorphism'de bilinçli tür dönüşümleri
//Daha iyi anlayabilmek için örnek;
object i = 123; //Bu 123 ayrıca bir int olabilir. Bunu objecten çıkarıp int atamak için önce unbox etmeliyiz
int i2 = (int)i;
Z z1 = (Z)x;
z1.z(); z1.x(); z1.y();
C#:
class Program
{
static void Main(string[] args)
{
A a = new A();
B b = new B();
A ab = new B();
a.Display(); // Output: Display from class A
b.Display(); // Output: Display from class B
ab.Display(); // Output: Display from class A (hides method in B)
Console.WriteLine("");
_A _a = new _A();
_B _b = new _B();
_A _ab = new _B();
_a.Display(); // Output: Display from class A
_b.Display(); // Output: Display from class B
_ab.Display(); // Output: Display from class B (because of polymorphism)
}
}
class A
{
public void Display()
{
Console.WriteLine("Display from class A");
}
}
class B : A
{
public new void Display() // Name hiding
{
Console.WriteLine("Display from class B");
}
}
class _A
{
public virtual void Display() // Virtual method
{
Console.WriteLine("Display from class A");
}
}
class _B : _A
{
public override void Display() // Override
{
Console.WriteLine("Display from class B");
}
}
23. :base(), :this()
C#:
class Program
{
static void Main(string[] args)
{
DerivedClass derived = new DerivedClass(5, "");
}
}
class BaseClass
{
public BaseClass(int a) { Console.WriteLine("base int a"); }
public BaseClass() { }
public BaseClass(double d, decimal dm) { }
}
class DerivedClass : BaseClass
{
public DerivedClass() : base(5) { Console.WriteLine("empty derived"); } //Base, bir sınıfın base class'ının constractorlarından hangisinin tetikleneceğini belirlememizi ve
//varsa parametrelerinin değerlerini derived classtan vermemizi sağlar
public DerivedClass(int a, string b) : this() { Console.WriteLine("derived int a, string b"); } //Bir sınıftaki constractorlar arasında geçiş yapmamızı sağlar
public DerivedClass(byte @byte) { }
}
24. Compile'da değil ancak run time hatası verecek trick bir kod.
C#:
class A { }
class B : A { }
class C : B { }
class Program
{
static void Main(string[] args)
{
A a = new B();
Console.WriteLine(a is C);
//C c = (C)a;
C c = a as C; //Bu derleyici hatası vermez, çünkü C'de a dan türemiş ama burada mantıksal adı a olan referans türlü değişken,
//B objesini/nesnesini işaret ettiği ve C'de B'nin kalıtım/poliformizim hiyerarşisi olarak altında bulunduğu için
//(C)a şeklinde yazarsak kod RunTime hatası verecektir. Böyle durumlarda hata almaktansa en azından null dönsün diye
//unboxing işlemlerini "as" operatörünü kullanmamız tavsiye ediliyor.
}
}
25. is-a/has-a/can do.
C#:
class B : A //--> Bu ayrıda B is a A diye de söylenebilir. Kalıtım varsa "..is a..";
class A { }
class B : A { C c; }
class C { } //Burada B is a A ayrıca B has a C. ..has a... => Bir sınıfın başka bir sınıfın nesnesine dair (o sınıfın referansı)
// //sahiplik ifadesi. Ayrıca buna "Composition" denmektedir.
//Can do: ilgili interface'i implement eden sınıf o interface'deki kabiliyetleri yapabileceğinden buna "can do" denir
interface ICanDo { void CanDoIlikisi(); }
class CanDoIlişkisClass : ICanDo
{
public void CanDoIlikisi()
{
//..
}
}
26.Mülakatta Çıkmış Sealed*
C#:
//sealed: Bir sınıfın kalıtımsal ilişki sırasında miras vermesini engelleyen bir keyworld'dür. Neticede recorlar da class fıratında olduğu için
//recordlarda da aynı işe yarar.
//Ayrıca override edilmiş virtual ile işaretlenmiş metotların hiyerarşikdeki sonraki sınıflar tarafından override edilmesini engellemek için de
//sealed keyworld 'ü kullanılır.
//Bir sınıf sealed ile işaretlendi derleyici o sınıfın miras veremeyeceğini bildiği için daha hızlı derleyerek ufak çapta bir performans artışı
//gözlemlenmiştir.
class A { public virtual void SealdTest() { } }
class B : A { public override void SealdTest() { /* Override edildi*/ } }
class C : B { public override void SealdTest() { /* Override edildi*/ } }
class D : C { public sealed override void SealdTest() { /* Override edildi ve seallendi */ } }
class E : D { /* Override edilemedi */ }
27. Partial.
C#:
//partial: nested class'larda da partial yapılanması kullanılabilir. İçine yazdığımı class partial olmak zorunda değil (tabii ki her şeyi aynıysa partial olmalı)
class Program
{
static void Main(string[] args)
{
V.M v = new V.M();
T.M t = new T.M();
t.Mesage();
v.Test();
}
}
class T
{
public partial class M
{
public void Mesage()
{
Console.WriteLine("hello");
}
}
}
class V
{
public partial class M
{
public M()
{
Console.WriteLine("this is ctor");
}
public void Test()
{
Console.WriteLine("test");
}
}
}
28. Teroik: Abstract Class - Interface.
abstract class'da partial yapılanması için class yazısından önce yani "abstract partial class" şeklinde
tanımlama yapmalıyız
Abstraction(soyutlama) bir operasyon anında, kullanılacak sınıfın sadece o operasyona ilişkin memberlarını getirmektir.
Bunu yapmak için sadece Interface veya Abstract Class ile sınırlı değiliz, abstraction bir davranıştır/mantıktır.
Ancak bunun için interface'leri kullanmak olayı çok daha pratik hale getirir.
Abstract class'lardan nesne üretebilir miyiz?
Abstract class'lar soyut yapılanmalar olduğu için yapısal olaraka iradeli bir şekilde (new operatörü vs.)
nesne üretilebilir bir tür değildir. Ancak bir abstract class bir sınıf tarafından implement edildiğinde
o sınıftan (new MyClass() ile nesne üretildiğinde kalıtımsal hiyerarşinin gereği asbtract class'dan da,
compiler tarafından, bir nesne üretilir
Abstrac Class'la Interface arasındaki farklardan biri de budur. Abstract class'da biz iradeli şekilde
üretemesek de compiler tarafından bir nesnesi üretilebilir, heap'de o nesne yer tutar ama
Interface'lerde bu da olmuyor. Interface türünden bir nesne mümkün değil
tanımlama yapmalıyız
Abstraction(soyutlama) bir operasyon anında, kullanılacak sınıfın sadece o operasyona ilişkin memberlarını getirmektir.
Bunu yapmak için sadece Interface veya Abstract Class ile sınırlı değiliz, abstraction bir davranıştır/mantıktır.
Ancak bunun için interface'leri kullanmak olayı çok daha pratik hale getirir.
Abstract class'lardan nesne üretebilir miyiz?
Abstract class'lar soyut yapılanmalar olduğu için yapısal olaraka iradeli bir şekilde (new operatörü vs.)
nesne üretilebilir bir tür değildir. Ancak bir abstract class bir sınıf tarafından implement edildiğinde
o sınıftan (new MyClass()
compiler tarafından, bir nesne üretilir
Abstrac Class'la Interface arasındaki farklardan biri de budur. Abstract class'da biz iradeli şekilde
üretemesek de compiler tarafından bir nesnesi üretilebilir, heap'de o nesne yer tutar ama
Interface'lerde bu da olmuyor. Interface türünden bir nesne mümkün değil
C#:
abstract class MyAbstractClass {
public MyAbstractClass() //Abstract olmayan bir yapı
{
Console.WriteLine("A Abstrac Class");
}
abstract public void AbstractBirYapı(); //Abstract yapı
}
abstract class AbstractClasslarArasıMiras : MyAbstractClass
{
//Bir abstract class başka bir abstract class'a miras verebilirler. Bu bir implementasyon değil,
//inheritance'dır. Abstract class birbirlerine içlerinde abstract olarak işaretlenmiş olan yapıları
//zoraki olarak kendilerini implement eden/concrete class'lara uygulattırırlar, diğer abstract class'lara
// değil
}
abstract class SadeceBirTaneAbstractClass
{ }
class _123MyClass : MyAbstractClass //,SadeceBirTaneAbstractClass: Bir sınıf en fazla bir tane sınıftan
//kalıtım alabilir, abstract class'da yapısal olarak bir sınıf olduğu için
//bir sınıf sadece bir tane abstract class'ı kalıtımsal olarak
//alabilir/implemente edebilir
{
public _123MyClass()
{
Console.WriteLine("_123MyClass Class");
}
public override void AbstractBirYapı()
{
//Yukarıda imzası zorunlu kılınan "kontrat"ın gövdesi.
//virtual ile işaretlenmiş class memberlar istenilirse override edilebilecekken abstract class'lardaki
//abstrac ile işaretlenmiş memberlar implement edildikleri sınıflarda zorunlu olarak override keyworld'ü
//ile yazılırlar
}
}
new _123MyClass();
29. Interfaceler.
Metotların içinde operasyonel işler yapılır dolayısyla struct, intface, class gibi inşai
şeyler metotların içinde tanımlanamaz
Field'lar metotlar ve property'ler tarafından operatif olarak kullanılırlar, bu yüzden
field'ların interfacelerde tanımlanması yasaklanmıştır
Interfaceler: sınıflarda zoraki olarak implement edilecek/uygulanacak yapıların imzalarının
barındığı bir şablon/kontrattır
Bir sınıf veya bir interface birden çok interface'den kalıtım alabilir/implement edebilir
Hem class hem de interface/ ler'den kalıtım alıcaksa :'dan sonra önce class sonra
interfaceler yazılır
şeyler metotların içinde tanımlanamaz
Field'lar metotlar ve property'ler tarafından operatif olarak kullanılırlar, bu yüzden
field'ların interfacelerde tanımlanması yasaklanmıştır
Interfaceler: sınıflarda zoraki olarak implement edilecek/uygulanacak yapıların imzalarının
barındığı bir şablon/kontrattır
Bir sınıf veya bir interface birden çok interface'den kalıtım alabilir/implement edebilir
Hem class hem de interface/ ler'den kalıtım alıcaksa :'dan sonra önce class sonra
interfaceler yazılır
30. Explicity Implement
C#:
class Program
{
static void Main(string[] args)
{
InterfaceNameHidingSampleClass sampleClass = new InterfaceNameHidingSampleClass();
FirstInterface firstInterface = sampleClass;
firstInterface.NameHidingMetot(); //İlk Interfacedeki aynı isimli metot
SecondInterface secondInterface = sampleClass;
secondInterface.NameHidingMetot(); //İkinci Interfacedeki aynı isimli metot
}
}
interface FirstInterface
{
void NameHidingMetot();
}
interface SecondInterface
{
void NameHidingMetot();
}
class InterfaceNameHidingSampleClass : FirstInterface, SecondInterface
//Explicity Implement, implement edilen interfaceler/metotlar arasında name hiding (aynı isim) durumu olunca açıkça
//hangi hangi metot kimden geliyor yazmaktır. Normalde concrete class'da interfacelerin imzasını public olarak
//yazıyorken Explicity implement durumunda private formda yazılır.
{
void FirstInterface.NameHidingMetot()
{
//Explicity Implement duruunda tüm bu duurmdakileri açıkca yazabileceğimiz gibi bir tanesini normal
//impleement edip geri kalanları Explicity Implement de edebilirdik
}
void SecondInterface.NameHidingMetot()
{
//..
}
}
31. Class ile Interface arasında "NameHiding", Derived Class hem Base class'dan hem de interface'den aynı isimli metot alıyorsa ne olur?
C#:
//Derived Class hem Base class'dan hem de interface'den aynı isimli metot alıyorsa ne olur?
interface SameNameWithClassInterface
{ void SameNameMothodWithClassAndInterface(); }
class SameNameWithInterfaceClass
{ public void SameNameMothodWithClassAndInterface() { } }
class ClassAndInterfaceSameNamedMethodCase: SameNameWithInterfaceClass, SameNameWithClassInterface
{
//Burada görüldüğü gibi interfacedeki zorunlu imzanın gövdesini oluşturmadık ama hata
//vermedi çünkü aynı isimli metot base class'dan geldiği için ona saydı.
//Bu durum da Name Hiding değildir
}
32. Default Implementation
C#:
interface DefaultImplement
{
void DefaultImplement()
{
Console.WriteLine("Bu interface'i implement eden sınıf eğer bu imza için gövde oluşturmazsa " +
"interface içinde oluşturduğumuz gövde varsayılan olarak kullanılacaktır");
}
void NonDefaultImplement();
}
class _MyClass_ : DefaultImplement
{
public void NonDefaultImplement()
{
//Diğer imzaya uygun gövde oluşturmadığımız için hata vermiyor. Eğer oluştursaydık default/varsayılan
//olan gövdeyi değil yeni oluşturduğumuz kullanılırdı
}
}
Ekstra bilgi: Delegate'ler object sınıfından türemez, Record'lar class'lardan kalıtım alamaz, record'lardan kalıtım alırlar, Statik Polimorfizm: Klasik metot overloading'de nesne referansına verdiğimiz parametre ile hangi overload metottun çalışacağı durumudur.
Dinamik Polimorfizm: Hangi metottun tetikleneceğinin run time'da belirlenmesi.
C#:
class Program
{
static void Main(string[] args)
{
Arac a = new Arac();
a.Calıstır();
Arac t = new Taksi();
t.Calıstır();
}
}
class Arac
{
public virtual void Calıstır()
{
Console.WriteLine("Araba çalışıyor");
}
}
class Taksi : Arac
{
public override void Calıstır()
{
Console.WriteLine("Taksi Çalışıyor");
}
}
İyi çalışmalar!
Son düzenleyen: Moderatör: