سؤال

ما هي أفضل الممارسات فيما يتعلق باستخدام وبنية الطبقات الداخلية في C#.

على سبيل المثال، إذا كان لدي فئة أساسية كبيرة جدًا وفئتان داخليتان كبيرتان، فهل يجب علي تقسيمها إلى ملفات تعليمات برمجية منفصلة (فئة جزئية) أو تركها كملف تعليمات برمجية واحد كبير جدًا وغير عملي؟

هل من الممارسات السيئة أيضًا أن يكون لديك فصل مجرد مع طبقة داخلية عامة موروثة؟

هل كانت مفيدة؟

المحلول

عادةً ما أقوم بحجز الفصول الداخلية لأحد غرضين:

  1. الفئات العامة المشتقة من فئتها الأصلية حيث تكون الفئة الأصلية عبارة عن تطبيق أساسي مجرد باستخدام طريقة مجردة واحدة أو أكثر وكل فئة فرعية عبارة عن تطبيق يخدم تطبيقًا محددًا. بعد قراءة تصميم إطار العمل والمبادئ التوجيهية، أرى أنه تم وضع علامة "تجنب" على هذا، إلا أنني أستخدمه في سيناريوهات مشابهة للتعدادات - على الرغم من أن هذا ربما يعطي انطباعًا سيئًا أيضًا

  2. تعتبر الفئات الداخلية خاصة وهي وحدات من منطق الأعمال أو مرتبطة بشكل وثيق بفئتها الأصلية بطريقة يتم من خلالها كسرها بشكل أساسي عند استهلاكها أو استخدامها من قبل أي فئة أخرى.

بالنسبة لجميع الحالات الأخرى، أحاول الاحتفاظ بها في نفس مساحة الاسم ونفس مستوى إمكانية الوصول مثل المستهلك/الأصل المنطقي - غالبًا بأسماء أقل ملاءمة قليلاً من الفئة "الرئيسية".

في المشاريع الكبيرة، ستفاجأ بعدد المرات التي قد تجد فيها نفسك تقوم في البداية ببناء مكون قوي الترابط لمجرد أن الغرض الأول أو الأساسي يجعله يبدو منطقيًا - ولكن ما لم يكن لديك سبب جيد أو فني جدًا لتأمينه وإخفائه من الأنظار فلا ضرر يذكر من تعريض الفصل حتى تتمكن المكونات الأخرى من استهلاكه.

يحرر ضع في اعتبارك أنه على الرغم من أننا نتحدث عن الفئات الفرعية، إلا أنه ينبغي أن تكون مكونات مصممة بشكل جيد ومترابطة بشكل أو بآخر.حتى لو كانت خاصة وغير مرئية للعالم الخارجي، فإن الحفاظ على الحد الأدنى من "المساحة السطحية" بين الفئات سيسهل إلى حد كبير إمكانية صيانة التعليمات البرمجية الخاصة بك للتوسع أو التغيير في المستقبل.

نصائح أخرى

ليس لدي الكتاب في متناول اليد، لكن إرشادات تصميم الإطار تقترح استخدامه public الطبقات الداخلية طالما لا يتعين على العملاء الإشارة إلى اسم الفئة. private الطبقات الداخلية جيدة:لن يلاحظ أحد هذه.

سيء: ListView.ListViewItemCollection collection = new ListView.ListViewItemCollection();

جيد: listView.Items.Add(...);

فيما يتعلق بفصلك الكبير:من المفيد عمومًا تقسيم شيء كهذا إلى فئات أصغر، تحتوي كل منها على وظيفة محددة واحدة.من الصعب الانفصال في البداية، لكني أتوقع أن ذلك سيجعل حياتك أسهل لاحقًا...

يجب أن تكون

وعموما الطبقات الداخلية الخاص والتي يمكن استخدامها فقط من قبل الطبقة التي تحتوي عليها. إذا كانت الطبقات الداخلية كبيرة جدا من شأنها أن تشير أنها ينبغي أن تكون فصول خاصة بهم.

وعادة عندما كنت قد حصلت على الطبقة الداخلية الكبيرة انها لأن الطبقة الداخلية يقترن بإحكام لانها تحتوي على طبقة ويحتاج الحصول على وسائل خاصة بها.

أعتقد أن هذا أمر شخصي إلى حد ما، ولكن من المحتمل أن أقوم بتقسيمها إلى ملفات تعليمات برمجية منفصلة عن طريق جعل فئة "المضيف" جزئية.

من خلال القيام بذلك، يمكنك الحصول على المزيد من النظرة العامة من خلال تحرير ملف المشروع لجعل مجموعة الملفات مثل فئات المصمم في Windows Forms.أعتقد أنني رأيت برنامجًا إضافيًا لـ Visual Studio يقوم بذلك تلقائيًا نيابةً عنك، لكنني لا أتذكر أين.

يحرر:
بعد بعض البحث وجدت الوظيفة الإضافية Visual Studio للقيام بذلك والتي تسمى VSCommands

وفيما يتعلق فقط كيفية تنظيم مثل هذا الوحش ...

ويمكنك استخدام فئات جزئية لتقسيم الطبقة الرئيسية والطبقات المتداخلة. عند القيام بذلك، وكنت نصحت لتسمية الملفات بشكل مناسب لذلك من الواضح ما يجري.

// main class in file Outer.cs
namespace Demo
{
  public partial class Outer
  {
     // Outer class
  }
}

// nested class in file Outer.Nested1.cs
namespace Demo
{
  public partial class Outer
  {
    private class Nested1
    {
      // Nested1 details
    }
  }
}

في الكثير بنفس الطريقة، كنت كثيرا ما نرى واجهات (صريحة) في ملف الخاصة بهم. مثلا Outer.ISomeInterface.cs بدلا من الافتراضي تحرير #regioning لهم.

والمشاريع الخاصة بك بنية الملف ثم يبدأ لتبدو وكأنها

   /Project/Demo/ISomeInterface.cs
   /Project/Demo/Outer.cs
   /Project/Demo/Outer.Nested1.cs
   /Project/Demo/Outer.ISomeInterface.cs

وعادة عندما نقوم به هذه انها لالاختلاف في نمط البناء.

وأود شخصيا أن يكون فئة واحدة لكل ملف، والطبقات الداخلية كجزء من هذا الملف. أعتقد الطبقات الداخلية يجب عادة (دائما تقريبا) تكون خاصة، وعلى تفصيل تطبيق الطبقة. وجود لهم في ملف منفصل يخلط بين الأشياء، IMO.

وعن طريق المناطق كود للالتفاف حول الطبقات الداخلية وإخفاء تفاصيلها يعمل بشكل جيد بالنسبة لي، في هذه الحالة، ويحافظ على ملف عن صعوبة التعامل معها. المناطق كود الحفاظ على الطبقة الداخلية "الخفية"، ومنذ ذلك انها تفصيل تطبيق الخاص، هذا شيء طيب معي.

وأنا شخصيا استخدام الطبقات الداخلية لتغليف بعض المفاهيم والعمليات المستخدمة فقط داخليا ضمن فئة. بهذه الطريقة أنا لا تلوث تلك الفئة "المعهد غير العام والحفاظ على المعهد نظيفة وصغيرة الحجم.

ويمكنك الاستفادة من فئات جزئية لتحريك تعريف هذه الطبقات الداخلية في ملف مختلف عن أفضل orgnanization. VS لا تلقائيا ملفات فئة جزئية المجموعة بالنسبة لك باستثناء بعض البنود templatized مثل ASP.NET، أشكال WinForm، وما إلى ذلك سوف تحتاج إلى تعديل ملف المشروع وإجراء بعض التغييرات في هناك. يمكنك أن تبحث في واحد من التجمع الموجودة في وجود لنرى كيف يتم ذلك. وأعتقد أن هناك بعض وحدات الماكرو التي تسمح لك لمجموعة جزئية ملفات فئة لك في حل اكسبلورر.

في رأيي الطبقات الداخلية، وإذا لزم الأمر في كل شيء، يجب أن تبقى صغيرة وتستخدم داخليا من قبل تلك الفئة فقط. إذا كنت تستخدم Relfector على .NET Framework سوف تراهم تستخدم الكثير فقط لهذا الغرض.

إذا الطبقات الداخلية الخاصة بك والحصول على كبيرة جدا وأود أن نقلها بالتأكيد للخروج الى طبقات منفصلة / codefiles بطريقة أو بأخرى إلا إذا كان لالصيانة. لا بد لي من دعم بعض التعليمات البرمجية الموجودة فيها شخص يعتقد أنها فكرة رائعة لاستخدام الطبقات الداخلية داخل الطبقات الداخلية. ذلك أدى إلى التسلسل الهرمي الطبقة الداخلية تعمل 4-5 مستويات عميقة. وغني عن القول هو رمز لا يمكن اختراقها، ويأخذ الأعمار لفهم ما كنت تبحث في.

ونرى هنا مثالا عمليا على فئة المتداخلة التي يمكن أن تعطيك فكرة عن استخدامها (إضافة بعض اختبار وحدة)

namespace CoreLib.Helpers
{
    using System;
    using System.Security.Cryptography;

    public static class Rnd
    {
        private static readonly Random _random = new Random();

        public static Random Generator { get { return _random; } }

        static Rnd()
        {
        }

        public static class Crypto
        {
            private static readonly RandomNumberGenerator _highRandom = RandomNumberGenerator.Create();

            public static RandomNumberGenerator Generator { get { return _highRandom; } }

            static Crypto()
            {
            }

        }

        public static UInt32 Next(this RandomNumberGenerator value)
        {
            var bytes = new byte[4];
            value.GetBytes(bytes);

            return BitConverter.ToUInt32(bytes, 0);
        }
    }
}

[TestMethod]
public void Rnd_OnGenerator_UniqueRandomSequence()
{
    var rdn1 = Rnd.Generator;
    var rdn2 = Rnd.Generator;
    var list = new List<Int32>();
    var tasks = new Task[10];
    for (var i = 0; i < 10; i++)
    {
        tasks[i] = Task.Factory.StartNew((() =>
        {
            for (var k = 0; k < 1000; k++)
            {
                lock (list)
                {
                    list.Add(Rnd.Generator.Next(Int32.MinValue, Int32.MaxValue));
                }
            }
        }));
    }
    Task.WaitAll(tasks);
    var distinct = list.Distinct().ToList();
    Assert.AreSame(rdn1, rdn2);
    Assert.AreEqual(10000, list.Count);
    Assert.AreEqual(list.Count, distinct.Count);
}

[TestMethod]
public void Rnd_OnCryptoGenerator_UniqueRandomSequence()
{
    var rdn1 = Rnd.Crypto.Generator;
    var rdn2 = Rnd.Crypto.Generator;
    var list = new ConcurrentQueue<UInt32>();
    var tasks = new Task[10];
    for (var i = 0; i < 10; i++)
    {
        tasks[i] = Task.Factory.StartNew((() =>
        {
            for (var k = 0; k < 1000; k++)
            {
                    list.Enqueue(Rnd.Crypto.Generator.Next());
            }
        }));
    }
    Task.WaitAll(tasks);
    var distinct = list.Distinct().ToList();
    Assert.AreSame(rdn1, rdn2);
    Assert.AreEqual(10000, list.Count);
    Assert.AreEqual(list.Count, distinct.Count);
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top