Aracılığıyla paylaş


C# ve .NET'te devralma özelliği

Bu öğreticide C# dilinde devralma işlemleri tanıtılmıştır. Devralma, belirli işlevler (veri ve davranış) sağlayan bir temel sınıf tanımlamanızı ve bu işlevselliği devralan veya geçersiz kılan türetilmiş sınıfları tanımlamanızı sağlayan nesne odaklı programlama dillerinin bir özelliğidir.

Önkoşullar

Yükleme yönergeleri

Windows'da tüm önkoşulları yüklemek için bu WinGet yapılandırma dosyası kullanılır. Zaten yüklü bir şey varsa WinGet bu adımı atlar.

  1. Dosyayı indirin ve çift tıklayarak çalıştırın.
  2. Lisans sözleşmesini okuyun, yyazın ve kabul etmek isteyip istemediğiniz sorulduğunda Enter seçin.
  3. Görev Çubuğunuzda yanıp sönen bir Kullanıcı Hesabı Denetimi (UAC) istemi alırsanız yüklemenin devam etmesi için izin verin.

Diğer platformlarda, bu bileşenlerin her birini ayrı ayrı yüklemeniz gerekir.

  1. Önerilen yükleyiciyi .NET SDK indirme sayfasından indirin ve çift tıklayarak çalıştırın. İndirme sayfası platformunuzu algılar ve platformunuz için en son yükleyiciyi önerir.
  2. Visual Studio Code giriş sayfasından en son yükleyiciyi indirin ve çift tıklayarak çalıştırın. Bu sayfa ayrıca platformunuzu algılar ve bağlantı sisteminiz için doğru olmalıdır.
  3. C# DevKit uzantısı sayfasındaki "Yükle" düğmesine tıklayın. Bu, Visual Studio code'u açar ve uzantıyı yüklemek mi yoksa etkinleştirmek mi istediğinizi sorar. "Yükle" seçeneğini belirleyin.

Örnekleri çalıştırma

Bu öğreticideki örnekleri oluşturmak ve çalıştırmak için komut satırından dotnet yardımcı programını kullanırsınız. Her örnek için şu adımları izleyin:

  1. Örneği depolamak için bir dizin oluşturun.

  2. Yeni bir .NET Core projesi oluşturmak için komut istemine dotnet new console komutunu girin.

  3. Örnekteki kodu kopyalayıp kod düzenleyicinize yapıştırın.

  4. Projenin bağımlılıklarını yüklemek veya geri yüklemek için komut satırından dotnet restore komutunu girin.

    dotnet restore, dotnet new, dotnet build, dotnet run, dotnet testve dotnet publishgibi geri yükleme gerektiren tüm komutlar tarafından örtük olarak çalıştırıldığından dotnet pack çalıştırmanız gerekmez. Örtük geri yüklemeyi devre dışı bırakmak için --no-restore seçeneğini kullanın.

    komutu, geri yüklemenin anlamlı olduğu bazı senaryolarda hala yararlıdır, örneğin Azure DevOps Services'teki sürekli tümleştirme derlemeleri veya geri yüklemenin ne zaman gerçekleştiğini açıkça kontrol etmesi gereken derleme sistemlerinde.

    NuGet akışlarını yönetme hakkında bilgi için belgelerine bakın.

  5. Örneği derlemek ve yürütmek için dotnet run komutunu girin.

Arka plan: Devralma nedir?

Devralma, nesne odaklı programlamanın temel özniteliklerinden biridir. Bir ana sınıfın davranışını yeniden kullanan (devralan), genişleten veya değiştiren bir alt sınıf tanımlamanıza olanak tanır. Üyeleri devralınan sınıf,temel sınıfı olarak adlandırılır. Temel sınıfın üyelerini devralan sınıf,türetilmiş sınıfı olarak adlandırılır.

C# ve .NET yalnızca tek devralmayı destekler. Yani, bir sınıf yalnızca tek bir sınıftan devralabilir. Ancak, devralma geçişlidir ve bir tür kümesi için devralma hiyerarşisi tanımlamanızı sağlar. Başka bir deyişle, D türü, Ctüründen devralabilir, bu, Btüründen devralır, bu da Atemel sınıf türünden devralır. Devralma geçişli olduğundan, A türü üyeleri Dtürü için kullanılabilir.

Temel sınıfın tüm üyeleri türetilmiş sınıflar tarafından devralınmaz. Aşağıdaki üyeler devralınmıyor:

Temel sınıfın diğer tüm üyeleri türetilmiş sınıflar tarafından devralınmış olsa da, görünür olup olmadıkları erişilebilirliklerine bağlıdır. Bir üyenin erişilebilirliği, türetilmiş sınıflar için görünürlüğünü aşağıdaki gibi etkiler:

  • Özel üyeler, yalnızca kendi temel sınıfı içinde iç içe yerleştirilmiş türetilmiş sınıflarda görülebilir. Aksi takdirde, türetilmiş sınıflarda görünmezler. Aşağıdaki örnekte, A.B, A'den türetilen bir iç içe geçmiş sınıftır ve C, A'ten türetilen başka bir sınıftır. Özel A._value alanı A.B.'de görünür. Ancak, açıklamaları C.GetValue yönteminden kaldırır ve örneği derlemeyi denerseniz, cs0122 derleyici hatası oluşturur: "'A._value', koruma düzeyi nedeniyle erişilemez."

    public class A
    {
        private int _value = 10;
    
        public class B : A
        {
            public int GetValue()
            {
                return _value;
            }
        }
    }
    
    public class C : A
    {
        //    public int GetValue()
        //    {
        //        return _value;
        //    }
    }
    
    public class AccessExample
    {
        public static void Main(string[] args)
        {
            var b = new A.B();
            Console.WriteLine(b.GetValue());
        }
    }
    // The example displays the following output:
    //       10
    
  • Korumalı üyeleri yalnızca türetilmiş sınıflarda görünür.

  • İç üyeleri yalnızca temel sınıfla aynı derlemede bulunan türetilmiş sınıflarda görünür. Bunlar, temel sınıftan farklı bir derlemede bulunan türetilmiş sınıflarda görünmez.

  • Genel üyeleri türetilmiş sınıflarda görünür ve türetilmiş sınıfın ortak arabiriminin bir parçasıdır. Kamuya açık devralınan üyeler, türetilmiş sınıfta tanımlanmış gibi çağrılabilir. Aşağıdaki örnekte, sınıf AMethod1adlı bir yöntem tanımlar ve sınıf BAsınıfından devralır. Örnek daha sonra Method1üzerinde bir örnek yöntemiymiş gibi B çağırır.

    public class A
    {
        public void Method1()
        {
            // Method implementation.
        }
    }
    
    public class B : A
    { }
    
    public class Example
    {
        public static void Main()
        {
            B b = new ();
            b.Method1();
        }
    }
    

Türetilmiş sınıflar da alternatif bir uygulama sağlayarak devralınan üyeleri eşitleyebilir. Bir üyeyi geçersiz kılabilmek için temel sınıftaki üyenin sanal anahtar sözcüğüyle işaretlenmesi gerekir. Varsayılan olarak, temel sınıf üyeleri virtual olarak işaretlenmez ve geçersiz kılınamaz. Sanal olmayan bir üyeyi geçersiz kılmaya çalışmak, aşağıdaki örnekte olduğu gibi, derleyici hatası CS0506'ya neden olur: "<üyesi>, sanal, soyut veya geçersiz kılma olarak işaretlenmediği için, devralınan <üyesi>'ü geçersiz kılamaz."

public class A
{
    public void Method1()
    {
        // Do something.
    }
}

public class B : A
{
    public override void Method1() // Generates CS0506.
    {
        // Do something else.
    }
}

Bazı durumlarda, türetilmiş bir sınıf , temel sınıf uygulamasını geçersiz kılmalıdır. soyut anahtar sözcüğüyle işaretlenmiş temel sınıf üyeleri, türetilmiş sınıfların bunları geçersiz kılmasını gerektirir. Aşağıdaki örneği derlemeye çalışmak, sınıf <>için hiçbir uygulama sağlamadığı için, "<sınıfı> kalıtılan soyut üye BüyeA.Method1"i uygulamıyor" hatasıyla birlikte CS0534 derleyici hatası oluşturur.

public abstract class A
{
    public abstract void Method1();
}

public class B : A // Generates CS0534.
{
    public void Method3()
    {
        // Do something.
    }
}

Devralma yalnızca sınıflar ve arabirimler için geçerlidir. Diğer tür kategorileri (yapılar, temsilciler ve numaralandırmalar) devralmayı desteklemez. Bu kurallar nedeniyle, aşağıdaki örnekte olduğu gibi kod derlemeye çalışmak derleyici hatası CS0527 oluşturur: "Arabirim listesindeki 'ValueType' türü bir arabirim değil." Hata iletisi, bir yapının uyguladığı arabirimleri tanımlayabilmenize rağmen devralma işleminin desteklenmediğini gösterir.

public struct ValueStructure : ValueType // Generates CS0527.
{
}

Gizli devralma

Tek devralma yoluyla devralabilecekleri türlerin yanı sıra, .NET tür sistemindeki tüm türler örtük olarak Object veya ondan türetilen bir türü devralır. Object'un ortak işlevi her tür tarafından kullanılabilir.

Örtük devralmanın ne anlama geldiğini görmek için, yalnızca boş bir sınıf tanımı olan SimpleClassyeni bir sınıf tanımlayalım:

public class SimpleClass
{ }

Daha sonra yansımayı kullanarak (bu tür hakkında bilgi almak için bir türün meta verilerini incelemenize olanak tanır) SimpleClass türüne ait üyelerin listesini alabilirsiniz. SimpleClass sınıfınızda herhangi bir üye tanımlamamış olmanıza rağmen, örnekten elde ettiğiniz çıkış aslında dokuz üyesi olduğunu gösterir. Bu üyelerden biri, C# derleyicisi tarafından SimpleClass türü için otomatik olarak sağlanan parametresiz (veya varsayılan) bir oluşturucudur. Kalan sekiz, .NET tür sistemindeki tüm sınıfların ve arabirimlerin örtük olarak devraldığı türü olan Objectüyesidir.

using System.Reflection;

public class SimpleClassExample
{
    public static void Main()
    {
        Type t = typeof(SimpleClass);
        BindingFlags flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public |
                             BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
        MemberInfo[] members = t.GetMembers(flags);
        Console.WriteLine($"Type {t.Name} has {members.Length} members: ");
        foreach (MemberInfo member in members)
        {
            string access = "";
            string stat = "";
            var method = member as MethodBase;
            if (method != null)
            {
                if (method.IsPublic)
                    access = " Public";
                else if (method.IsPrivate)
                    access = " Private";
                else if (method.IsFamily)
                    access = " Protected";
                else if (method.IsAssembly)
                    access = " Internal";
                else if (method.IsFamilyOrAssembly)
                    access = " Protected Internal ";
                if (method.IsStatic)
                    stat = " Static";
            }
            string output = $"{member.Name} ({member.MemberType}): {access}{stat}, Declared by {member.DeclaringType}";
            Console.WriteLine(output);
        }
    }
}
// The example displays the following output:
//	Type SimpleClass has 9 members:
//	ToString (Method):  Public, Declared by System.Object
//	Equals (Method):  Public, Declared by System.Object
//	Equals (Method):  Public Static, Declared by System.Object
//	ReferenceEquals (Method):  Public Static, Declared by System.Object
//	GetHashCode (Method):  Public, Declared by System.Object
//	GetType (Method):  Public, Declared by System.Object
//	Finalize (Method):  Internal, Declared by System.Object
//	MemberwiseClone (Method):  Internal, Declared by System.Object
//	.ctor (Constructor):  Public, Declared by SimpleClass

Object sınıfından örtük devralma, bu yöntemleri SimpleClass sınıfı için kullanılabilir hale getirir:

  • Genel ToString yöntemi, bir SimpleClass nesnesini dize gösterimine dönüştürerek tam tür adını döndürür. Bu durumda, ToString yöntemi "SimpleClass" dizesini döndürür.

  • İki nesnenin eşitliğini test eden üç yöntem: ortak örnek Equals(Object) yöntemi, genel statik Equals(Object, Object) yöntemi ve genel statik ReferenceEquals(Object, Object) yöntemi. Varsayılan olarak, bu yöntemler başvuru eşitliğini test eder; yani eşit olmak için iki nesne değişkeninin aynı nesneye başvurması gerekir.

  • Türün bir örneğinin karma koleksiyonlarda kullanılabilmesini sağlayan bir değeri hesaplayan public GetHashCode yöntemi.

  • GetType türünü temsil eden bir Type nesnesi döndüren public SimpleClass yöntemi.

  • Bir nesnenin belleği çöp toplayıcı tarafından geri alınmadan önce yönetilmeyen kaynakları serbest bırakmak için tasarlanmış korumalı Finalize yöntemi.

  • Geçerli nesnenin sığ bir kopyasını oluşturan korumalı MemberwiseClone yöntemi.

Örtük devralma nedeniyle, bir SimpleClass nesneden devralınan herhangi bir üyeyi, aslında SimpleClass sınıfında tanımlanmış bir üye gibi çağırabilirsiniz. Örneğin, aşağıdaki örnek SimpleClass.ToString'den devralan SimpleClassObject yöntemini çağırır.

public class EmptyClass
{ }

public class ClassNameExample
{
    public static void Main()
    {
        EmptyClass sc = new();
        Console.WriteLine(sc.ToString());
    }
}
// The example displays the following output:
//        EmptyClass

Aşağıdaki tabloda, C# dilinde oluşturabileceğiniz türlerin kategorileri ve örtük olarak devraldıkları türler listelenmiştir. Her temel tür, devralma aracılığıyla örtük olarak türetilmiş türler için farklı bir üye kümesi sağlar.

Tür kategorisi Örtük olarak devralır:
sınıf Object
yapı ValueType, Object
sabit listesi Enum, ValueType, Object
temsilci MulticastDelegate, Delegate, Object

Kalıtım ve "bir tür" ilişkisi

Normalde devralma, temel sınıf ile türetilmiş sınıfların temel sınıfın özel sürümleri olduğu bir veya daha fazla türetilmiş sınıf arasındaki "is a" ilişkisini ifade etmek için kullanılır; türetilmiş sınıf, temel sınıfın bir türüdür. Örneğin, Publication sınıfı herhangi bir yayın türünü temsil eder ve Book ve Magazine sınıfları belirli yayın türlerini temsil eder.

Uyarı

Bir sınıf veya yapı bir veya daha fazla arabirim uygulayabilir. Arabirim uygulaması genellikle tek devralma için geçici bir çözüm olarak veya yapılarla devralmayı kullanmanın bir yolu olarak sunulsa da, arabirim ile uygulama türü arasında devralma yerine farklı bir ilişki (bir "yapabilir" ilişkisi) ifade etmek amaçlanmıştır. Arabirim, işlevselliğin bir alt kümesini tanımlar (eşitliği test etme, nesneleri karşılaştırma veya sıralama ya da kültüre duyarlı ayrıştırma ve biçimlendirmeyi destekleme gibi) arabirimin uygulama türleri için kullanılabilir hale getirdiği.

"is a" ifadesinin bir tür ile bu türün belirli bir örneği arasındaki ilişkiyi de ifade ettiğini unutmayın. Aşağıdaki örnekte, Automobile üç benzersiz salt okunur özelliğe sahip bir sınıftır: Makeotomobil üreticisi; Model, otomobil türü; ve Year, üretim yılı. Automobile sınıfınız ayrıca bağımsız değişkenleri özellik değerlerine atanmış bir oluşturucuya sahiptir ve Object.ToString sınıfı yerine Automobile örneğini benzersiz olarak tanımlayan bir dize oluşturmak için Automobile yöntemini geçersiz kılar.

public class Automobile
{
    public Automobile(string make, string model, int year)
    {
        if (make == null)
            throw new ArgumentNullException(nameof(make), "The make cannot be null.");
        else if (string.IsNullOrWhiteSpace(make))
            throw new ArgumentException("make cannot be an empty string or have space characters only.");
        Make = make;

        if (model == null)
            throw new ArgumentNullException(nameof(model), "The model cannot be null.");
        else if (string.IsNullOrWhiteSpace(model))
            throw new ArgumentException("model cannot be an empty string or have space characters only.");
        Model = model;

        if (year < 1857 || year > DateTime.Now.Year + 2)
            throw new ArgumentException("The year is out of range.");
        Year = year;
    }

    public string Make { get; }

    public string Model { get; }

    public int Year { get; }

    public override string ToString() => $"{Year} {Make} {Model}";
}

Bu durumda, belirli araba markalarını ve modellerini temsil etmek için devralmaya güvenmemelisiniz. Örneğin, Packard Motorlu Otomobil Şirketi tarafından üretilen otomobilleri temsil etmek için bir Packard türü tanımlamanız gerekmez. Bunun yerine, aşağıdaki örnekte olduğu gibi sınıf oluşturucusna geçirilen uygun değerlere sahip bir Automobile nesnesi oluşturarak bunları temsil edebilirsiniz.

using System;

public class Example
{
    public static void Main()
    {
        var packard = new Automobile("Packard", "Custom Eight", 1948);
        Console.WriteLine(packard);
    }
}
// The example displays the following output:
//        1948 Packard Custom Eight

Kalıtıma dayalı bir is-a ilişkisi, en iyi temel sınıfa ve bu sınıfa ek üyeler ekleyen ya da bu sınıfta bulunmayan ek işlevsellik gereksinim duyan türetilmiş sınıflara uygulanır.

Temel sınıfı ve türetilmiş sınıfları tasarlama

Şimdi temel sınıfı ve türetilmiş sınıflarını tasarlama işlemine göz atalım. Bu bölümde kitap, dergi, gazete, dergi, makale vb. her türlü yayını temsil eden Publicationtemel sınıfını tanımlayacaksınız. Ayrıca, Book'den türetilen bir Publication sınıfı da tanımlayacaksınız. Örneği kolayca genişleterek Magazine, Journal, Newspaperve Articlegibi diğer türetilmiş sınıfları tanımlayabilirsiniz.

Temel Yayın sınıfı

Publication sınıfınızı tasarlarken birkaç tasarım kararı almanız gerekir:

  • Temel Publication sınıfınıza eklenecek üyeler ve Publication üyelerinin yöntem uygulamaları sağlayıp sağlamadığı veya Publication türetilmiş sınıfları için şablon olarak hizmet veren soyut bir temel sınıf olup olmadığı.

    Bu durumda, Publication sınıfı yöntem uygulamaları sağlar. Soyut temel sınıflar tasarlama ve türetilmiş sınıfları bölümü, türetilmiş sınıfların geçersiz kılması gereken yöntemleri tanımlamak için soyut bir temel sınıf kullanan bir örnek içerir. Türetilmiş sınıflar türetilen türe uygun herhangi bir uygulama sağlamak için ücretsizdir.

    Kodu yeniden kullanabilme (yani, birden çok türetilmiş sınıf, temel sınıf yöntemlerinin bildirimini ve uygulamasını paylaşır ve bunları geçersiz kılmaya gerek yoktur) soyut olmayan temel sınıfların bir avantajıdır. Bu nedenle, kodlarının bazı veya çoğu özel Publication türleri tarafından paylaşılma olasılığı varsa üyeleri Publication'a eklemeniz gerekir. Temel sınıf uygulamalarını verimli bir şekilde sağlamazsanız, temel sınıfta tek bir uygulama yerine türetilmiş sınıflarda büyük ölçüde özdeş üye uygulamaları sağlamanız gerekir. Yinelenen kodu birden çok konumda tutma gereksinimi olası bir hata kaynağıdır.

    Hem kodun yeniden kullanılmasını en üst düzeye çıkarmak hem de mantıksal ve sezgisel bir devralma hiyerarşisi oluşturmak için, Publication sınıfına yalnızca tümü veya çoğu yayın için ortak olan verileri ve işlevleri eklediğinizden emin olmak istersiniz. Daha sonra türetilmiş sınıflar, temsil ettikleri belirli yayın türlerine özgü üyeleri uygular.

  • Sınıf hiyerarşinizi ne kadar genişletirsiniz? Yalnızca bir temel sınıf ve bir veya daha fazla türetilmiş sınıf yerine üç veya daha fazla sınıf hiyerarşisi geliştirmek istiyor musunuz? Örneğin, Publication, Periodical'in üst sınıfı olabilir ve Periodicalde sırasıyla Journal, Newspaper ve 'ün üst sınıfı olabilir.

    Örneğiniz için, bir Publication sınıfının küçük hiyerarşisini ve Booksınıfından tek bir türetilmiş sınıfı kullanacaksınız. Örneği kolayca genişleterek, Publication'dan türeyen Magazine ve Articlegibi bir dizi ek sınıf oluşturabilirsiniz.

  • Temel sınıfın örneğini oluşturmanın mantıklı olup olmadığını. Aksi takdirde, sınıfa soyut anahtar sözcüğünü uygulamanız gerekir. Aksi takdirde, Publication sınıfınız sınıf oluşturucu çağrılarak örneklenebilir. Sınıf oluşturucusunun doğrudan çağrısıyla abstract anahtar sözcüğüyle işaretlenmiş bir sınıfın örneğini oluşturmaya çalışılırsa, C# derleyicisi CS0144 "Soyut sınıfın veya arabirimin örneği oluşturulamıyor" hatasını oluşturur. Sınıfın örneğini oluşturmak için yansıma kullanılarak bir deneme yapılırsa, yansıma yöntemi bir MemberAccessExceptionoluşturur.

    Varsayılan olarak, bir temel sınıf sınıf oluşturucu çağrılarak örneklenebilir. Açıkça bir sınıf oluşturucu tanımlamanız gerekmez. Temel sınıfın kaynak kodunda yoksa, C# derleyicisi otomatik olarak bir varsayılan (parametresiz) oluşturucu sağlar.

    Örneğinizde, Publication sınıfını somutlaştırılamaması için soyut olarak işaretleyeceksiniz. abstract yöntemleri olmayan bir abstract sınıfı, bu sınıfın birkaç somut sınıf (Book, Journalgibi) arasında paylaşılan soyut bir kavramı temsil ettiğini gösterir.

  • Türetilmiş sınıfların belirli üyelerin temel sınıf uygulamasını devralması gerekip gerekmediği, temel sınıf uygulamasını geçersiz kılma seçeneğine sahip olup olmadıkları veya bir uygulama sağlamaları gerekip gerekmediği. Türetilmiş sınıfları bir uygulama sağlamaya zorlamak için soyut anahtar sözcüğünü kullanırsınız. Türetilmiş sınıfların temel sınıf yöntemini geçersiz kabilmesi için sanal anahtar sözcüğünü kullanırsınız. Varsayılan olarak, temel sınıfta tanımlanan yöntemler geçersiz kılınamaz.

    Publication sınıfının herhangi bir abstract yöntemi yoktur, ancak sınıfın kendisi abstract.

  • Türetilmiş bir sınıfın devralma hiyerarşisindeki son sınıfı temsil edip etmediği ve ek türetilmiş sınıflar için temel sınıf olarak kullanılıp kullanılamayacağı. Varsayılan olarak, herhangi bir sınıf temel sınıf olarak hizmet verebilir. Bir sınıfın herhangi bir ek sınıf için temel sınıf olarak hizmet veremeyeceğini belirtmek için korumalı anahtar sözcüğünü uygulayabilirsiniz. Mühürlü bir sınıftan türetme girişimi, "CS0509: <typeName>mühürlü türünden türetilemiyor" derleyici hatasına yol açtı.

    Örneğin, türetilmiş sınıfınızı sealedolarak işaretleyeceksiniz.

Aşağıdaki örnekte, Publication sınıfının kaynak kodunun yanı sıra PublicationType özelliği tarafından döndürülen bir Publication.PublicationType numaralandırması gösterilmektedir. Object'den devraldığı üyelere ek olarak, Publication sınıfı aşağıdaki benzersiz üyeleri ve üye geçersiz kılmaları tanımlar.


public enum PublicationType { Misc, Book, Magazine, Article };

public abstract class Publication
{
    private bool _published = false;
    private DateTime _datePublished;
    private int _totalPages;

    public Publication(string title, string publisher, PublicationType type)
    {
        if (string.IsNullOrWhiteSpace(publisher))
            throw new ArgumentException("The publisher is required.");
        Publisher = publisher;

        if (string.IsNullOrWhiteSpace(title))
            throw new ArgumentException("The title is required.");
        Title = title;

        Type = type;
    }

    public string Publisher { get; }

    public string Title { get; }

    public PublicationType Type { get; }

    public string? CopyrightName { get; private set; }

    public int CopyrightDate { get; private set; }

    public int Pages
    {
        get { return _totalPages; }
        set
        {
            if (value <= 0)
                throw new ArgumentOutOfRangeException(nameof(value), "The number of pages cannot be zero or negative.");
            _totalPages = value;
        }
    }

    public string GetPublicationDate()
    {
        if (!_published)
            return "NYP";
        else
            return _datePublished.ToString("d");
    }

    public void Publish(DateTime datePublished)
    {
        _published = true;
        _datePublished = datePublished;
    }

    public void Copyright(string copyrightName, int copyrightDate)
    {
        if (string.IsNullOrWhiteSpace(copyrightName))
            throw new ArgumentException("The name of the copyright holder is required.");
        CopyrightName = copyrightName;

        int currentYear = DateTime.Now.Year;
        if (copyrightDate < currentYear - 10 || copyrightDate > currentYear + 2)
            throw new ArgumentOutOfRangeException($"The copyright year must be between {currentYear - 10} and {currentYear + 1}");
        CopyrightDate = copyrightDate;
    }

    public override string ToString() => Title;
}
  • Oluşturucu

    Publication sınıfı abstractolduğundan, aşağıdaki örnekte olduğu gibi doğrudan koddan örneği oluşturulamaz:

    var publication = new Publication("Tiddlywinks for Experts", "Fun and Games",
                                      PublicationType.Book);
    

    Ancak, örnek oluşturucu, Book sınıfının kaynak kodunda gösterildiği gibi doğrudan türetilmiş sınıf oluşturucularından çağrılabilir.

  • Yayınla ilgili iki özellik

    Title, değeri String oluşturucu çağrılarak sağlanan salt okunur bir Publication özelliğidir.

    Pages, yayının toplam sayfa sayısını gösteren bir okuma-yazma Int32 özelliğidir. Değer, totalPagesadlı özel bir alanda depolanır. Pozitif bir sayı olmalı aksi takdirde ArgumentOutOfRangeException hatası verilir.

  • Yayımcıyla ilgili üyeler

    İki salt okunur özellik: Publisher ve Type. Değerler başlangıçta Publication sınıf oluşturucusunun çağrısı tarafından sağlanır.

  • Yayımlamayla ilgili üyeler

    Publish ve GetPublicationDateiki yöntem yayın tarihini ayarlayıp döndürür. Publish yöntemi çağrıldığında özel published bayrağını true olarak ayarlar ve argüman olarak geçirilen tarihi özel datePublished alanına atar. GetPublicationDate yöntemi, published bayrağı falseise "NYP" dizesini ve datePublishedise true alanının değerini döndürür.

  • Telif haklarıyla ilgili üyeler

    Copyright yöntemi, telif hakkı sahibinin adını ve telif hakkı yılını bağımsız değişken olarak alır ve bunları CopyrightName ve CopyrightDate özelliklerine atar.

  • ToString yöntemini geçersiz kılma

    Bir tür Object.ToString yöntemini geçersiz kılmazsa, türün tam adını döndürür; bu, bir örneği diğerinden ayırt etmek için çok az kullanılır. Publication sınıfı, Object.ToString özelliğinin değerini döndürmek için Title geçersiz kılar.

Aşağıdaki şekilde temel Publication sınıfınızla örtük olarak devralınan Object sınıfı arasındaki ilişki gösterilmektedir.

Nesne ve Yayın sınıfları

Book sınıfı

Book sınıfı, bir kitabı özel bir yayın türü olarak temsil eder. Aşağıdaki örnekte, Book sınıfının kaynak kodu gösterilmektedir.

using System;

public sealed class Book : Publication
{
    public Book(string title, string author, string publisher) :
           this(title, string.Empty, author, publisher)
    { }

    public Book(string title, string isbn, string author, string publisher) : base(title, publisher, PublicationType.Book)
    {
        // isbn argument must be a 10- or 13-character numeric string without "-" characters.
        // We could also determine whether the ISBN is valid by comparing its checksum digit
        // with a computed checksum.
        //
        if (!string.IsNullOrEmpty(isbn))
        {
            // Determine if ISBN length is correct.
            if (!(isbn.Length == 10 | isbn.Length == 13))
                throw new ArgumentException("The ISBN must be a 10- or 13-character numeric string.");
            if (!ulong.TryParse(isbn, out _))
                throw new ArgumentException("The ISBN can consist of numeric characters only.");
        }
        ISBN = isbn;

        Author = author;
    }

    public string ISBN { get; }

    public string Author { get; }

    public decimal Price { get; private set; }

    // A three-digit ISO currency symbol.
    public string? Currency { get; private set; }

    // Returns the old price, and sets a new price.
    public decimal SetPrice(decimal price, string currency)
    {
        if (price < 0)
            throw new ArgumentOutOfRangeException(nameof(price), "The price cannot be negative.");
        decimal oldValue = Price;
        Price = price;

        if (currency.Length != 3)
            throw new ArgumentException("The ISO currency symbol is a 3-character string.");
        Currency = currency;

        return oldValue;
    }

    public override bool Equals(object? obj)
    {
        if (obj is not Book book)
            return false;
        else
            return ISBN == book.ISBN;
    }

    public override int GetHashCode() => ISBN.GetHashCode();

    public override string ToString() => $"{(string.IsNullOrEmpty(Author) ? "" : Author + ", ")}{Title}";
}

Publication'den devraldığı üyelere ek olarak, Book sınıfı aşağıdaki benzersiz üyeleri ve üye geçersiz kılmaları tanımlar.

  • İki oluşturucu

    İki Book oluşturucu üç ortak parametreyi paylaşır. İki başlık ve yayımcı, Publication oluşturucusunun parametrelerine karşılık gelir. Üçüncüsü, herkese açık değişmez özelliğine depolanan Author'dır. Oluşturuculardan biri, otomatik özelliğinde depolanan bir ISBN parametresi içerir.

    İlk oluşturucu, diğer oluşturucuyu çağırmak için bu anahtar sözcüğünü kullanır. Oluşturucu zincirleme, oluşturucuları tanımlamada yaygın bir desendir. Daha az parametreye sahip oluşturucular, oluşturucuyu en fazla sayıda parametreyle çağırırken varsayılan değerler sağlar.

    İkinci oluşturucu, başlık ve yayımcı adını temel sınıf oluşturucusna geçirmek için temel anahtar sözcüğünü kullanır. Kaynak kodunuzda bir temel sınıf oluşturucusunda açık bir çağrı yapmazsanız, C# derleyicisi otomatik olarak temel sınıfın varsayılan veya parametresiz oluşturucusunun çağrısını sağlar.

  • Salt okunur ISBN özelliği, Book nesnesinin Uluslararası Standart Kitap Numarası'nı, yani benzersiz bir 10 veya 13 basamaklı numarayı döndürür. ISBN, Book oluşturucularından birine parametre olarak sağlanır. ISBN, derleyici tarafından otomatik olarak oluşturulan özel bir yedekleme alanında depolanır.

  • Salt okunur bir Author özelliği. Yazar adı, her iki Book oluşturucusuna bağımsız değişken olarak verilir ve özellikte depolanır.

  • Price ve Currency, sadece okunabilen fiyat ile ilgili iki özelliktir. Değerleri bir SetPrice metot çağrısında argüman olarak sağlanır. Currency özelliği üç basamaklı ISO para birimi simgesidir (örneğin, ABD doları için USD). ISO para birimi simgeleri ISOCurrencySymbol özelliğinden alınabilir. Bu özelliklerin her ikisi de harici olarak salt okunur durumdadır, ancak her ikisi de Book sınıfındaki kod tarafından ayarlanabilir.

  • SetPrice ve Price özelliklerinin değerlerini ayarlayan bir Currency yöntemi. Bu değerler aynı özellikler tarafından döndürülür.

  • ToString yöntemine (Publication'den devralınan) ve Object.Equals(Object) ve GetHashCode yöntemlerine (Object'ten devralınır) geçersiz kılmalar.

    Geçersiz kılınmadığı sürece, Object.Equals(Object) yöntemi, başvuruların eşitliğini test eder. Diğer bir ifadeyle, aynı nesneye başvuruda bulunan iki nesne değişkeninin eşit olduğu kabul edilir. Öte yandan, Book sınıfında, aynı ISBN'ye sahip olmaları durumunda iki Book nesne eşit olmalıdır.

    Object.Equals(Object) yöntemini geçersiz kıldığınızda, çalışma zamanının öğeleri verimli bir şekilde almak için karma koleksiyonlarda depolamak için kullandığı bir değeri döndüren GetHashCode yöntemini de geçersiz kılmanız gerekir. Karma kod, eşitlik testiyle tutarlı bir değer döndürmelidir. İki Object.Equals(Object) nesnesinin ISBN özellikleri eşitse true döndürmek üzere Book metodunu geçersiz kıldığınız için, GetHashCode özelliği tarafından döndürülen dizenin ISBN metodunu çağırarak hesaplanan karma kodu döndürürsünüz.

Aşağıdaki şekilde, Book sınıfı ile onun temel sınıfı olan Publicationarasındaki ilişki gösterilmektedir.

Yayın ve Kitap sınıfları

Artık bir Book nesnesi oluşturabilir, hem kendine özgü hem de devralınan üyelerini çağırabilir ve aşağıdaki örnekte gösterildiği gibi Publication türünde veya Booktüründe bir parametre bekleyen bir yönteme argüman olarak aktarabilirsiniz.

public class ClassExample
{
    public static void Main()
    {
        var book = new Book("The Tempest", "0971655819", "Shakespeare, William",
                            "Public Domain Press");
        ShowPublicationInfo(book);
        book.Publish(new DateTime(2016, 8, 18));
        ShowPublicationInfo(book);

        var book2 = new Book("The Tempest", "Classic Works Press", "Shakespeare, William");
        Console.Write($"{book.Title} and {book2.Title} are the same publication: " +
              $"{((Publication)book).Equals(book2)}");
    }

    public static void ShowPublicationInfo(Publication pub)
    {
        string pubDate = pub.GetPublicationDate();
        Console.WriteLine($"{pub.Title}, " +
                  $"{(pubDate == "NYP" ? "Not Yet Published" : "published on " + pubDate):d} by {pub.Publisher}");
    }
}
// The example displays the following output:
//        The Tempest, Not Yet Published by Public Domain Press
//        The Tempest, published on 8/18/2016 by Public Domain Press
//        The Tempest and The Tempest are the same publication: False

Soyut temel sınıflar ve türetilmiş sınıfları tasarlama

Önceki örnekte, türetilmiş sınıfların kodu paylaşmasına izin vermek için bir dizi yöntem için bir uygulama sağlayan bir temel sınıf tanımlamıştınız. Ancak çoğu durumda temel sınıfın bir uygulama sağlaması beklenmemektedir. Bunun yerine, temel sınıf soyut yöntemleri bildiren bir soyut sınıf; türetilmiş her sınıfın uygulaması gereken üyeleri tanımlayan bir şablon görevi görür. Genellikle soyut bir temel sınıfta türetilen her türün uygulanması bu türe özeldir. Sınıfı soyut anahtar sözcüğüyle işaretlediniz çünkü bir Publication nesnesinin örneğini oluşturmak mantıklı değildi, ancak sınıf yayınlar için ortak işlevsellik uygulamaları sağlamıştı.

Örneğin, her kapalı iki boyutlu geometrik şekil iki özellik içerir: alan, şeklin iç kapsamı; ve çevre veya şeklin kenarları boyunca mesafe. Ancak bu özelliklerin hesaplanma şekli tamamen belirli şekle bağlıdır. Örneğin, bir dairenin çevresini (veya çevresini) hesaplama formülü, karenin formülünden farklıdır. Shape sınıfı, abstract yöntemleri olan bir abstract sınıfıdır. Bu, türetilmiş sınıfların aynı işlevselliği paylaştığını gösterir, ancak bu türetilmiş sınıflar bu işlevselliği farklı şekilde uygular.

Aşağıdaki örnek, iki özelliği tanımlayan Shape adlı bir soyut temel sınıf tanımlar: Area ve Perimeter. Sınıfı soyut anahtar sözcüğüyle işaretlemeye ek olarak, her örnek üyesi de soyut anahtar sözcüğüyle işaretlenir. Bu durumda, Shape, tam adı yerine türün adını döndürecek şekilde Object.ToString yöntemini de geçersiz kılar. Ayrıca, çağıranların türetilmiş herhangi bir sınıfın örneğinin alanını ve çevresini kolayca almasını sağlayan GetArea ve GetPerimeteriki statik üye tanımlar. Türetilmiş bir sınıfın örneğini bu yöntemlerden herhangi birine geçirdiğinizde, çalışma zamanı türetilmiş sınıfın yöntem geçersiz kılma işlemini çağırır.

public abstract class Shape
{
    public abstract double Area { get; }

    public abstract double Perimeter { get; }

    public override string ToString() => GetType().Name;

    public static double GetArea(Shape shape) => shape.Area;

    public static double GetPerimeter(Shape shape) => shape.Perimeter;
}

Daha sonra belirli şekilleri temsil eden Shape bazı sınıfları türetebilirsiniz. Aşağıdaki örnek, Square, Rectangleve Circleüç sınıfı tanımlar. Her biri, alanı ve çevreyi hesaplamak için söz konusu şekil için benzersiz bir formül kullanır. Türetilmiş sınıflardan bazıları, temsil ettikleri şekle özgü Rectangle.Diagonal ve Circle.Diametergibi özellikleri de tanımlar.

using System;

public class Square : Shape
{
    public Square(double length)
    {
        Side = length;
    }

    public double Side { get; }

    public override double Area => Math.Pow(Side, 2);

    public override double Perimeter => Side * 4;

    public double Diagonal => Math.Round(Math.Sqrt(2) * Side, 2);
}

public class Rectangle : Shape
{
    public Rectangle(double length, double width)
    {
        Length = length;
        Width = width;
    }

    public double Length { get; }

    public double Width { get; }

    public override double Area => Length * Width;

    public override double Perimeter => 2 * Length + 2 * Width;

    public bool IsSquare() => Length == Width;

    public double Diagonal => Math.Round(Math.Sqrt(Math.Pow(Length, 2) + Math.Pow(Width, 2)), 2);
}

public class Circle : Shape
{
    public Circle(double radius)
    {
        Radius = radius;
    }

    public override double Area => Math.Round(Math.PI * Math.Pow(Radius, 2), 2);

    public override double Perimeter => Math.Round(Math.PI * 2 * Radius, 2);

    // Define a circumference, since it's the more familiar term.
    public double Circumference => Perimeter;

    public double Radius { get; }

    public double Diameter => Radius * 2;
}

Aşağıdaki örnek, Shape'den türetilen nesneleri kullanır. Shape türetilmiş nesnelerden oluşan bir dizi örneği oluşturur ve dönüş Shape özellik değerlerini sarmalayan Shape sınıfının statik yöntemlerini çağırır. Çalışma zamanı, türetilmiş sınıfların geçersiz kılınan özelliklerinden değerleri alır. Örnek ayrıca dizideki her Shape nesnesini türetilmiş türüne dönüştürür ve atama başarılı olursa, Shape'nin söz konusu alt sınıfının özelliklerini alır.

using System;

public class Example
{
    public static void Main()
    {
        Shape[] shapes = { new Rectangle(10, 12), new Square(5),
                    new Circle(3) };
        foreach (Shape shape in shapes)
        {
            Console.WriteLine($"{shape}: area, {Shape.GetArea(shape)}; " +
                              $"perimeter, {Shape.GetPerimeter(shape)}");
            if (shape is Rectangle rect)
            {
                Console.WriteLine($"   Is Square: {rect.IsSquare()}, Diagonal: {rect.Diagonal}");
                continue;
            }
            if (shape is Square sq)
            {
                Console.WriteLine($"   Diagonal: {sq.Diagonal}");
                continue;
            }
        }
    }
}
// The example displays the following output:
//         Rectangle: area, 120; perimeter, 44
//            Is Square: False, Diagonal: 15.62
//         Square: area, 25; perimeter, 20
//            Diagonal: 7.07
//         Circle: area, 28.27; perimeter, 18.85