.Net'te neden / ne zaman yuvalanmış sınıflar kullanmalısınız? Yoksa yapmamalı mısın?


95

In Kathleen Dollard 2008 blog post , o .net iç içe geçmiş sınıflar kullanmak için ilginç bir nedeni sunar. Ancak, FxCop'un iç içe geçmiş sınıfları sevmediğini de belirtiyor. FxCop kurallarını yazan kişilerin aptal olmadığını varsayıyorum, bu yüzden bu pozisyonun arkasında mantık var, ama bulamadım.


Blog gönderisine Wayback Arşiv bağlantısı: web.archive.org/web/20141127115939/https://blogs.msmvps.com/…
iokevins

Nawfal'ın işaret ettiği gibi , arkadaşımız Eric Lippert burada bu sorunun bir kopyasını yanıtladı , söz konusu cevap " Sınıf dışında anlamsız olan bir yardımcı sınıfa ihtiyaç duyduğunuzda iç içe sınıfları kullanın; özellikle iç içe geçmiş sınıflardan yararlanabildiğinde Dış sınıfın özel uygulama ayrıntıları.¶ İç içe geçmiş sınıfların işe yaramaz olduğuna dair argümanınız da özel yöntemlerin işe yaramaz olduğuna dair bir argüman ... "
2019'da

Yanıtlar:


97

İç içe yerleştirdiğiniz sınıf yalnızca çevreleyen sınıf için yararlı olduğunda yuvalanmış bir sınıf kullanın. Örneğin, iç içe geçmiş sınıflar şöyle bir şey yazmanıza izin verir (basitleştirilmiş):

public class SortedMap {
    private class TreeNode {
        TreeNode left;
        TreeNode right;
    }
}

Tek bir yerde sınıfınızın tam bir tanımını yapabilirsiniz, sınıfınızın nasıl çalıştığını tanımlamak için herhangi bir PIMPL çemberinden atlamanız gerekmez ve dış dünyanın uygulamanızın hiçbirini görmesi gerekmez.

TreeNode sınıfı harici olsaydı, ya tüm alanları publicyapmanız ya da get/setonu kullanmak için bir sürü yöntem oluşturmanız gerekirdi . Dış dünya, akıllarını kirleten başka bir sınıfa sahip olacaktı.


44
Buna eklemek için: Kodunuzu biraz daha iyi yönetmek için kısmi sınıfları ayrı dosyalarda da kullanabilirsiniz. İç sınıfı ayrı bir dosyaya koyun (bu durumda SortedMap.TreeNode.cs). Bu, kodunuzu temiz tutarken aynı zamanda kodunuzu ayrı tutacaktır :)
Erik van Brakel

1
Bir public API'nin dönüş türünde veya konteyner sınıfının bir genel mülkünde kullanılıyorsa, yuvalanmış sınıfı genel veya dahili yapmanız gereken durumlar olacaktır. Yine de iyi bir uygulama olup olmadığından emin değilim. Bu tür durumlar, yuvalanmış sınıfı kapsayıcı sınıfının dışına çıkarmak daha mantıklı olabilir. .Net çerçevesindeki System.Windows.Forms.ListViewItem.ListViewSubItem sınıfı böyle bir örnektir.
MHK

16

Sun'ın Java Eğiticisinden:

Neden İç İçe Sınıflar Kullanılmalı? İç içe sınıfları kullanmanın birkaç zorlayıcı nedeni vardır, bunların arasında:

  • Yalnızca tek bir yerde kullanılan sınıfları mantıksal olarak gruplamanın bir yoludur.
  • Kapsüllenmeyi artırır.
  • İç içe geçmiş sınıflar, daha okunabilir ve sürdürülebilir koda yol açabilir.

Sınıfların mantıksal gruplaması — Bir sınıf yalnızca bir başka sınıf için yararlıysa, o sınıfın içine yerleştirmek ve ikisini bir arada tutmak mantıklıdır. Bu tür "yardımcı sınıfların" iç içe yerleştirilmesi, paketlerini daha modern hale getirir.

Arttırılmış kapsülleme — B'nin, aksi takdirde özel olarak ilan edilecek olan A üyelerine erişime ihtiyaç duyduğu A ve B olmak üzere iki üst düzey sınıfı düşünün. B sınıfını A sınıfı içinde saklayarak, A'nın üyeleri özel ilan edilebilir ve B bunlara erişebilir. Ek olarak, B'nin kendisi dış dünyadan gizlenebilir. <- Bu, C # 'ın iç içe geçmiş sınıflar uygulaması için geçerli değildir, bu yalnızca Java için geçerlidir.

Daha okunaklı, bakımı kolay kod - En üst düzey sınıflar içindeki küçük sınıfları yerleştirmek, kodu kullanıldığı yere daha yakın yerleştirir.


1
Java'da olduğu gibi, C # içindeki çevreleyen sınıftan örnek değişkenlerine erişemediğiniz için bu gerçekten geçerli değildir. Yalnızca statik üyelere erişilebilir.
Ben Baron

5
Ancak, çevreleyen sınıfın bir örneğini yuvalanmış sınıfa geçirirseniz, yuvalanmış sınıf bu örnek değişkeni aracılığıyla tüm üyelere tam erişime sahip olur ... yani gerçekten, Java'nın örnek değişkenini örtük yapması gibi, oysa C # için açık hale getirin.
Alex

@Alex Hayır değil, Java'da iç içe geçmiş sınıf, somutlaştırıldığında aslında ana sınıf örneğini yakalar - diğer şeylerin yanı sıra bu, ebeveynin çöplerin toplanmasını engellediği anlamına gelir. Ayrıca, yuvalanmış sınıfın üst sınıf olmadan somutlaştırılamayacağı anlamına gelir. Yani hayır, bunlar hiç de aynı değil.
Tomáš ZATO - Eski Monica

2
@ TomášZato Açıklamam oldukça uygun, gerçekten. Java'daki yuvalanmış sınıflarda etkili bir şekilde örtük bir üst örnek değişkeni bulunurken, C #'da örneği iç sınıfa açıkça vermeniz gerekir. Bunun bir sonucu, sizin de dediğiniz gibi, Java'nın iç sınıflarının bir ebeveyn örneğine sahip olması gerekirken C # 'ın olmamasıdır. Her halükarda benim ana noktam, C # 'ın iç sınıflarının aynı zamanda ebeveynlerinin özel alanlarına ve özelliklerine erişebilmesi, ancak bunu yapabilmek için ebeveyn örneğinden açıkça geçirilmesi gerektiğiydi.
Alex

9

Tamamen Tembel ve iş parçacığı güvenli tekli desen

public sealed class Singleton
{
    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }
    
    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }

        internal static readonly Singleton instance = new Singleton();
    }
}

kaynak: https://csharpindepth.com/Articles/Singleton


5

Kullanıma bağlıdır. Nadiren Public yuvalanmış bir sınıf kullanırdım ama her zaman Private yuvalanmış sınıfları kullanırdım. Özel bir yuvalanmış sınıf, yalnızca üst öğenin içinde kullanılması amaçlanan bir alt nesne için kullanılabilir. Buna bir örnek, bir HashTable sınıfının verileri yalnızca dahili olarak depolamak için özel bir Giriş nesnesi içermesidir.

Sınıfın arayan tarafından (harici olarak) kullanılması amaçlanıyorsa, genellikle onu ayrı bir bağımsız sınıf yapmayı seviyorum.


5

Yukarıda listelenen diğer nedenlere ek olarak, yalnızca yuvalanmış sınıfları değil, aslında genel yuvalanmış sınıfları kullanmayı düşünmem için bir neden daha var. Aynı genel tür parametrelerini paylaşan birden çok genel sınıfla çalışanlar için, genel bir ad alanı bildirme yeteneği son derece yararlı olacaktır. Ne yazık ki, .Net (veya en azından C #) genel ad alanları fikrini desteklemiyor. Yani aynı amacı gerçekleştirmek için, aynı hedefi gerçekleştirmek için genel sınıfları kullanabiliriz. Mantıksal bir varlıkla ilgili aşağıdaki örnek sınıfları alın:

public  class       BaseDataObject
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

public  class       BaseDataObjectList
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
:   
                    CollectionBase<tDataObject>
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

public  interface   IBaseBusiness
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

public  interface   IBaseDataAccess
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

Bu sınıfların imzalarını genel bir ad alanı kullanarak basitleştirebiliriz (yuvalanmış sınıflar aracılığıyla uygulanır):

public
partial class   Entity
                <
                    tDataObject, 
                    tDataObjectList, 
                    tBusiness, 
                    tDataAccess
                >
        where   tDataObject     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObject
        where   tDataObjectList : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObjectList, new()
        where   tBusiness       : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseBusiness
        where   tDataAccess     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseDataAccess
{

    public  class       BaseDataObject {}

    public  class       BaseDataObjectList : CollectionBase<tDataObject> {}

    public  interface   IBaseBusiness {}

    public  interface   IBaseDataAccess {}

}

Ardından, Erik van Brakel'in daha önceki bir yorumda önerdiği gibi kısmi sınıfların kullanımı yoluyla, sınıfları ayrı iç içe dosyalara ayırabilirsiniz. Kısmi sınıf dosyalarını iç içe yerleştirmeyi desteklemek için NestIn gibi bir Visual Studio uzantısı kullanmanızı öneririm. Bu, "ad alanı" sınıf dosyalarının, iç içe geçmiş sınıf dosyalarını klasör benzeri bir şekilde organize etmek için de kullanılmasına izin verir.

Örneğin:

Entity.cs

public
partial class   Entity
                <
                    tDataObject, 
                    tDataObjectList, 
                    tBusiness, 
                    tDataAccess
                >
        where   tDataObject     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObject
        where   tDataObjectList : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObjectList, new()
        where   tBusiness       : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseBusiness
        where   tDataAccess     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseDataAccess
{
}

Entity.BaseDataObject.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  class   BaseDataObject
    {

        public  DataTimeOffset  CreatedDateTime     { get; set; }
        public  Guid            CreatedById         { get; set; }
        public  Guid            Id                  { get; set; }
        public  DataTimeOffset  LastUpdateDateTime  { get; set; }
        public  Guid            LastUpdatedById     { get; set; }

        public
        static
        implicit    operator    tDataObjectList(DataObject dataObject)
        {
            var returnList  = new tDataObjectList();
            returnList.Add((tDataObject) this);
            return returnList;
        }

    }

}

Entity.BaseDataObjectList.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  class   BaseDataObjectList : CollectionBase<tDataObject>
    {

        public  tDataObjectList ShallowClone() 
        {
            var returnList  = new tDataObjectList();
            returnList.AddRange(this);
            return returnList;
        }

    }

}

Entity.IBaseBusiness.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  interface   IBaseBusiness
    {
        tDataObjectList Load();
        void            Delete();
        void            Save(tDataObjectList data);
    }

}

Entity.IBaseDataAccess.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  interface   IBaseDataAccess
    {
        tDataObjectList Load();
        void            Delete();
        void            Save(tDataObjectList data);
    }

}

Görsel stüdyo çözüm gezginindeki dosyalar şu şekilde düzenlenir:

Entity.cs
+   Entity.BaseDataObject.cs
+   Entity.BaseDataObjectList.cs
+   Entity.IBaseBusiness.cs
+   Entity.IBaseDataAccess.cs

Ve genel ad alanını aşağıdaki gibi uygularsınız:

User.cs

public
partial class   User
:
                Entity
                <
                    User.DataObject, 
                    User.DataObjectList, 
                    User.IBusiness, 
                    User.IDataAccess
                >
{
}

User.DataObject.cs

partial class   User
{

    public  class   DataObject : BaseDataObject 
    {
        public  string  UserName            { get; set; }
        public  byte[]  PasswordHash        { get; set; }
        public  bool    AccountIsEnabled    { get; set; }
    }

}

User.DataObjectList.cs

partial class   User
{

    public  class   DataObjectList : BaseDataObjectList {}

}

User.IBusiness.cs

partial class   User
{

    public  interface   IBusiness : IBaseBusiness {}

}

User.IDataAccess.cs

partial class   User
{

    public  interface   IDataAccess : IBaseDataAccess {}

}

Ve dosyalar çözüm gezgininde şu şekilde organize edilecektir:

User.cs
+   User.DataObject.cs
+   User.DataObjectList.cs
+   User.IBusiness.cs
+   User.IDataAccess.cs

Yukarıdaki, bir dış sınıfı genel bir ad alanı olarak kullanmanın basit bir örneğidir. Geçmişte 9 veya daha fazla tür parametresi içeren "genel ad alanları" oluşturdum. Bu tür parametrelerini, özellikle yeni bir parametre eklerken, tür parametrelerini bilmek için gereken dokuz tür arasında senkronize tutmak zorunda kalmak sıkıcıydı. Genel ad alanlarının kullanılması, bu kodu çok daha yönetilebilir ve okunabilir hale getirir.


3

Katheleen'in makalesini doğru anlarsam, EntityCollection <SomeEntity> yerine SomeEntity.Collection yazabilmek için iç içe geçmiş sınıf kullanmayı önerir. Bana göre bu, sizi yazmadan kurtarmanın tartışmalı bir yolu. Gerçek dünyada uygulama koleksiyonlarının uygulamalarda bazı farklılıklar olacağından eminim, bu yüzden yine de ayrı bir sınıf oluşturmanız gerekecek. Diğer sınıf kapsamlarını sınırlamak için sınıf adını kullanmanın iyi bir fikir olmadığını düşünüyorum. Zekayı kirletir ve sınıflar arasındaki bağımlılıkları güçlendirir. Ad alanlarının kullanılması, sınıf kapsamını kontrol etmenin standart bir yoludur. Bununla birlikte, @hazzen yorumundaki gibi iç içe geçmiş sınıfların kullanımının, kötü tasarımın bir işareti olan tonlarca iç içe geçmiş sınıfınız olmadığı sürece kabul edilebilir olduğunu düşünüyorum.


1

Uygulama ayrıntılarını gizlemek için genellikle iç içe geçmiş sınıfları kullanırım. Eric Lippert'in cevabından bir örnek:

abstract public class BankAccount
{
    private BankAccount() { }
    // Now no one else can extend BankAccount because a derived class
    // must be able to call a constructor, but all the constructors are
    // private!
    private sealed class ChequingAccount : BankAccount { ... }
    public static BankAccount MakeChequingAccount() { return new ChequingAccount(); }
    private sealed class SavingsAccount : BankAccount { ... }
}

Bu model, jeneriklerin kullanımıyla daha da iyi hale gelir. İki harika örnek için bu soruya bakın . Bu yüzden yazmayı bıraktım

Equality<Person>.CreateComparer(p => p.Id);

onun yerine

new EqualityComparer<Person, int>(p => p.Id);

Ayrıca genel bir listeye sahip olabilirim Equality<Person>ama olamazEqualityComparer<Person, int>

var l = new List<Equality<Person>> 
        { 
         Equality<Person>.CreateComparer(p => p.Id),
         Equality<Person>.CreateComparer(p => p.Name) 
        }

buna karşılık

var l = new List<EqualityComparer<Person, ??>>> 
        { 
         new EqualityComparer<Person, int>>(p => p.Id),
         new EqualityComparer<Person, string>>(p => p.Name) 
        }

imkansız. Üst sınıftan iç içe geçmiş sınıfın miras almasının avantajı budur.

Diğer bir durum (aynı nitelikte - uygulamayı gizleme), bir sınıfın üyelerini (alanlar, özellikler vb.) Yalnızca tek bir sınıf için erişilebilir hale getirmek istediğiniz zamandır:

public class Outer 
{
   class Inner //private class
   {
       public int Field; //public field
   }

   static inner = new Inner { Field = -1 }; // Field is accessible here, but in no other class
}

1

Yuvalanmış sınıflar için henüz bahsedilmeyen bir başka kullanım, genel türlerin ayrılmasıdır. Örneğin, çeşitli parametrelerle yöntemler alabilen ve bu parametrelerin bazılarının değerleriyle birlikte bazı genel statik sınıf ailelerine sahip olmak ve daha az parametreli temsilciler oluşturmak istediğini varsayalım. Örneğin, bir istek bir sunar statik bir yöntem olması Action<string, int, double>ve beyaz bir String<string, int>şekilde 3.5 geçen verilen işlem arayacak olan double; aynı zamanda, bir an alabilen Action<string, int, double>ve veren Action<string>, 7olarak intve 5.3olarak geçen statik bir yönteme sahip olmak isteyebilir double. Genel iç içe geçmiş sınıfları kullanarak, yöntem çağrılarının aşağıdaki gibi olması düzenlenebilir:

MakeDelegate<string,int>.WithParams<double>(theDelegate, 3.5);
MakeDelegate<string>.WithParams<int,double>(theDelegate, 7, 5.3);

veya, her ifadedeki son türler çıkarılabildiği için, ilk olanlar şunları yapamasa bile:

MakeDelegate<string,int>.WithParams(theDelegate, 3.5);
MakeDelegate<string>.WithParams(theDelegate, 7, 5.3);

İç içe geçmiş genel türleri kullanmak, hangi temsilcilerin genel tür açıklamasının hangi bölümlerine uygulanabileceğini anlamayı mümkün kılar.


1

Yuvalanmış sınıflar aşağıdaki ihtiyaçlar için kullanılabilir:

  1. Verilerin sınıflandırılması
  2. Ana sınıfın mantığı karmaşık olduğunda ve sınıfı yönetmek için alt nesnelere ihtiyaç duyduğunuzu hissettiğinizde
  3. Sınıfın durumu ve varlığının tamamen kapsayıcı sınıfa bağlı olduğunu düşündüğünüzde


0

Tek bir sınıfa özgü istisnaları iç içe geçirmeyi seviyorum, yani. başka bir yerden asla atılmayanlar.

Örneğin:

public class MyClass
{
    void DoStuff()
    {
        if (!someArbitraryCondition)
        {
            // This is the only class from which OhNoException is thrown
            throw new OhNoException(
                "Oh no! Some arbitrary condition was not satisfied!");
        }
        // Do other stuff
    }

    public class OhNoException : Exception
    {
        // Constructors calling base()
    }
}

Bu, proje dosyalarınızı derli toplu tutmanıza yardımcı olur ve yüzlerce küçük istisna sınıfıyla dolu değildir.


0

İç içe geçmiş sınıfı test etmeniz gerekeceğini unutmayın. Gizli ise, tek başına test edemezsiniz.

Yine de InternalsVisibleToöznitelikle bağlantılı olarak içsel hale getirebilirsiniz . Bununla birlikte, bu, özel bir alanı yalnızca test amacıyla dahili yapmakla aynı şey olacaktır, ben bunu kötü kendi kendine belgeleme olarak değerlendiriyorum.

Bu nedenle, yalnızca düşük karmaşıklık içeren iç içe geçmiş özel sınıfları uygulamak isteyebilirsiniz.


0

evet bu durum için:

class Join_Operator
{

    class Departamento
    {
        public int idDepto { get; set; }
        public string nombreDepto { get; set; }
    }

    class Empleado
    {
        public int idDepto { get; set; }
        public string nombreEmpleado { get; set; }
    }

    public void JoinTables()
    {
        List<Departamento> departamentos = new List<Departamento>();
        departamentos.Add(new Departamento { idDepto = 1, nombreDepto = "Arquitectura" });
        departamentos.Add(new Departamento { idDepto = 2, nombreDepto = "Programación" });

        List<Empleado> empleados = new List<Empleado>();
        empleados.Add(new Empleado { idDepto = 1, nombreEmpleado = "John Doe." });
        empleados.Add(new Empleado { idDepto = 2, nombreEmpleado = "Jim Bell" });

        var joinList = (from e in empleados
                        join d in departamentos on
                        e.idDepto equals d.idDepto
                        select new
                        {
                            nombreEmpleado = e.nombreEmpleado,
                            nombreDepto = d.nombreDepto
                        });
        foreach (var dato in joinList)
        {
            Console.WriteLine("{0} es empleado del departamento de {1}", dato.nombreEmpleado, dato.nombreDepto);
        }
    }
}

Neden? Gelecekteki okuyucuların cevabınızın ardındaki mantığı anlamalarına yardımcı olmak için çözümünüzdeki koda biraz bağlam ekleyin.
Grant Miller

0

Bu kavramı anlayışıma dayanarak, sınıflar kavramsal olarak birbirleriyle ilişkili olduğunda bu özelliği kullanabiliriz. Demek istediğim, bunlardan bazıları, iş mantığını tamamlamak için toplu bir kök nesneye yardımcı olan DDD dünyasında var olan varlıklar gibi işimizde eksiksiz bir Öğedir.

Açıklığa kavuşturmak için bunu bir örnekle göstereceğim:

Order ve OrderItem gibi iki sınıfımız olduğunu hayal edin. Sipariş sınıfında tüm orderItems ürünlerini yöneteceğiz ve OrderItem'de açıklama için tek bir siparişle ilgili verileri tutuyoruz, aşağıdaki sınıfları görebilirsiniz:

 class Order
    {
        private List<OrderItem> _orderItems = new List<OrderItem>();

        public void AddOrderItem(OrderItem line)
        {
            _orderItems.Add(line);
        }

        public double OrderTotal()
        {
            double total = 0;
            foreach (OrderItem item in _orderItems)
            {
                total += item.TotalPrice();
            }

            return total;
        }

        // Nested class
        public class OrderItem
        {
            public int ProductId { get; set; }
            public int Quantity { get; set; }
            public double Price { get; set; }
            public double TotalPrice() => Price * Quantity;
        }
    }

    class Program
    {

        static void Main(string[] args)
        {
            Order order = new Order();

            Order.OrderItem orderItem1 = new Order.OrderItem();
            orderItem1.ProductId = 1;
            orderItem1.Quantity = 5;
            orderItem1.Price = 1.99;
            order.AddOrderItem(orderItem1);

            Order.OrderItem orderItem2 = new Order.OrderItem();
            orderItem2.ProductId = 2;
            orderItem2.Quantity = 12;
            orderItem2.Price = 0.35;
            order.AddOrderItem(orderItem2);

            Console.WriteLine(order.OrderTotal());
            ReadLine();
        }


    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.