c# - هل يجب أن يكون استخدام "التوجيهات" داخل أو خارج مساحة الاسم؟




.net namespaces (6)

إنها ممارسة أفضل إذا كان هذا الإعداد الافتراضي باستخدام " المراجع " المستخدمة في حل المصدر الخاص بك يجب أن يكون خارج مساحات الأسماء وأن تلك "المرجع المضاف الجديد" هي ممارسة جيدة يجب عليك وضعها داخل مساحة الاسم. هذا هو التمييز بين ما تتم إضافته من المراجع.

لقد قمت بتشغيل StyleCop على بعض رمز C # ، وتحافظ على الإبلاغ عن أن التوجيهات using يجب أن تكون داخل مساحة الاسم.

هل هناك سبب تقني لوضع التوجيهات المستخدمة داخل بدلاً من خارج مساحة الاسم؟


هناك مشكلة في وضع استخدام العبارات داخل مساحة الاسم عندما تريد استخدام الأسماء المستعارة. لا يستفيد الاسم المستعار من عبارات using السابقة ويجب أن يكون مؤهلاً تمامًا.

يعتبر:

namespace MyNamespace
{
    using System;
    using MyAlias = System.DateTime;

    class MyClass
    {
    }
}

مقابل:

using System;

namespace MyNamespace
{
    using MyAlias = DateTime;

    class MyClass
    {
    }
}

يمكن أن يكون هذا واضحًا بشكل خاص إذا كان لديك اسم مستعار طويل المواءمة مثل التالي (وهو كيف وجدت المشكلة):

using MyAlias = Tuple<Expression<Func<DateTime, object>>, Expression<Func<TimeSpan, object>>>;

using العبارات داخل مساحة الاسم ، فإنه يصبح فجأة:

using MyAlias = System.Tuple<System.Linq.Expressions.Expression<System.Func<System.DateTime, object>>, System.Linq.Expressions.Expression<System.Func<System.TimeSpan, object>>>;

ليست جميلة.



وفقا لوثائق StyleCop:

SA1200: UsingDirectivesMustBePlacedWithinNamespace

السبب يتم وضع AC # باستخدام التوجيه خارج عنصر مساحة الاسم.

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

على سبيل المثال ، قد ينتج عن التعليمة البرمجية التالية انتهاكان لهذه القاعدة.

using System;
using Guid = System.Guid;

namespace Microsoft.Sample
{
    public class Program
    {
    }
}

ومع ذلك ، لن ينتج عن التعليمة البرمجية التالية أي انتهاكات لهذه القاعدة:

namespace Microsoft.Sample
{
    using System;
    using Guid = System.Guid;

    public class Program
    {
    }
}

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

namespace Microsoft.Sample
{
    using Guid = System.Guid;
    public class Guid
    {
        public Guid(string s)
        {
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            Guid g = new Guid("hello");
        }
    }
}

فشل التعليمة البرمجية على خطأ المحول البرمجي التالي، العثور على السطر الذي يحتوي على Guid g = new Guid("hello");

CS0576: Namespace 'Microsoft.Sample' يحتوي على تعريف يتعارض مع الاسم المستعار 'Guid'

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

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

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

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

  1. مساحات الأسماء المتعددة

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

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

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


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

أولاً ، تذكر أن إعلان مساحة اسم بنقاط ، مثل:

namespace MyCorp.TheProduct.SomeModule.Utilities
{
    ...
}

يعادل تمامًا ما يلي:

namespace MyCorp
{
    namespace TheProduct
    {
        namespace SomeModule
        {
            namespace Utilities
            {
                ...
            }
        }
    }
}

إذا كنت ترغب في ذلك ، يمكنك وضع التوجيهات على جميع هذه المستويات. (بالطبع ، نريد using s في مكان واحد فقط ، ولكن سيكون قانونيًا وفقًا للغة.)

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

الآن ، دعونا نكون واضحين حول ما يعنيه هذا في مثال ملموس مع الاتفاقيتين الرئيسيتين.

(1) مع استخدامات خارج:

using System;
using System.Collections.Generic;
using System.Linq;
//using MyCorp.TheProduct;  <-- uncommenting this would change nothing
using MyCorp.TheProduct.OtherModule;
using MyCorp.TheProduct.OtherModule.Integration;
using ThirdParty;

namespace MyCorp.TheProduct.SomeModule.Utilities
{
    class C
    {
        Ambiguous a;
    }
}

في الحالة الموضحة أعلاه ، لمعرفة ما هو نوع Ambiguous ، يتم البحث في هذا الترتيب:

  1. الأنواع المتداخلة داخل C (بما في ذلك الأنواع المتداخلة الموروثة)
  2. أنواع في مساحة الاسم الحالية MyCorp.TheProduct.SomeModule.Utilities
  3. أنواع في مساحة الاسم MyCorp.TheProduct.SomeModule
  4. أنواع في MyCorp.TheProduct
  5. أنواع في MyCorp
  6. أنواع في مساحة الاسم الخالية (مساحة الاسم العام)
  7. أنواع في System ، System.Collections.Generic ، System.Linq ، MyCorp.TheProduct.OtherModule ، MyCorp.TheProduct.OtherModule.Integration و ThirdParty

الاتفاقية الأخرى:

(2) مع استخدامات داخل:

namespace MyCorp.TheProduct.SomeModule.Utilities
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using MyCorp.TheProduct;                           // MyCorp can be left out; this using is NOT redundant
    using MyCorp.TheProduct.OtherModule;               // MyCorp.TheProduct can be left out
    using MyCorp.TheProduct.OtherModule.Integration;   // MyCorp.TheProduct can be left out
    using ThirdParty;

    class C
    {
        Ambiguous a;
    }
}

الآن ، يتم البحث عن نوع Ambiguous بهذا الترتيب:

  1. الأنواع المتداخلة داخل C (بما في ذلك الأنواع المتداخلة الموروثة)
  2. أنواع في مساحة الاسم الحالية MyCorp.TheProduct.SomeModule.Utilities
  3. أنواع في System ، System.Collections.Generic ، System.Linq ، MyCorp.TheProduct ، MyCorp.TheProduct.OtherModule ، MyCorp.TheProduct.OtherModule.Integration ، و ThirdParty
  4. أنواع في مساحة الاسم MyCorp.TheProduct.SomeModule
  5. أنواع في MyCorp
  6. أنواع في مساحة الاسم الخالية (مساحة الاسم العام)

(لاحظ أن MyCorp.TheProduct كان جزءًا من "3." ولذلك لم تكن هناك حاجة إليه بين "4" و "5".)

ملاحظات ختامية

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

أيضًا ، إذا كانت مساحة الاسم المتداخلة لها نفس الاسم كنوع ، فقد يتسبب ذلك في حدوث مشكلات.

من الخطر دائمًا نقل الاستخدامات من موقع إلى آخر بسبب تغيير التسلسل الهرمي للبحث ، ويمكن العثور على نوع آخر. لذلك ، اختر اتفاقية واحدة والتزم بها ، حتى لا تضطر إلى تحريك الاستخدامات من أي وقت مضى.

افتراضيًا ، تضع قوالب Visual Studio الأغراض خارج مساحة الاسم (على سبيل المثال ، إذا قمت بإنشاء VS إنشاء فئة جديدة في ملف جديد).

ميزة واحدة (صغيرة) من وجود استخدامات خارجية هي أنه يمكنك عندئذ استخدام التوجيهات المستخدمة للحصول على سمة عامة ، على سبيل المثال [assembly: ComVisible(false)] بدلاً من [assembly: System.Runtime.InteropServices.ComVisible(false)] .


يوجد في الواقع فرق (دقيق) بين الاثنين. تخيل أن لديك التعليمة البرمجية التالية في File1.cs:

// File1.cs
using System;
namespace Outer.Inner
{
    class Foo
    {
        static void Bar()
        {
            double d = Math.PI;
        }
    }
}

تخيل الآن أن شخصًا ما يضيف ملفًا آخر (File2.cs) إلى المشروع الذي يشبه هذا:

// File2.cs
namespace Outer
{
    class Math
    {
    }
}

يبحث المحول البرمجي Outer قبل النظر إلى تلك using توجيهات خارج مساحة الاسم ، لذلك يجد Outer.Math بدلاً من System.Math . لسوء الحظ (أو ربما لحسن الحظ؟) ، ليس لدى Outer.Math أي عضو PI ، لذلك يتم الآن كسر File1.

يتغير هذا إذا وضعت using داخل تعريف مساحة الاسم الخاص بك ، كما يلي:

// File1b.cs
namespace Outer.Inner
{
    using System;
    class Foo
    {
        static void Bar()
        {
            double d = Math.PI;
        }
    }
}

الآن يبحث المترجم System قبل البحث Outer ، يجد System.Math ، وكل شيء على ما يرام.

قد يجادل البعض بأن Math قد تكون اسمًا سيئًا لفئة محددة من قبل المستخدم ، نظرًا لأن هناك بالفعل واحدًا في System ؛ النقطة هنا هي فقط أن هناك اختلافًا ، ويؤثر على إمكانية صيانة رمزك.

من المهم أيضًا ملاحظة ما يحدث إذا كان Foo في مساحة الاسم Outer ، بدلاً من Outer.Inner . في هذه الحالة ، إضافة Outer.Math في File2 فواصل File1 بغض النظر عن أين يذهب using . هذا يعني أن المحول البرمجي يبحث في منطقة إحاطة إحاطة بداخله قبل أن يبحث في أي using التوجيه.







code-organization