Pertanyaan Haruskah 'menggunakan' arahan berada di dalam atau di luar ruang nama?


Saya telah berlari StyleCop atas beberapa kode C #, dan terus melaporkan bahwa saya using arahan harus berada di dalam namespace.

Apakah ada alasan teknis untuk menempatkan using arahan di dalam bukannya di luar namespace?


1737
2017-09-24 03:49


asal


Jawaban:


Sebenarnya ada perbedaan (halus) antara keduanya. Bayangkan Anda memiliki kode berikut di File1.cs:

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

Sekarang bayangkan bahwa seseorang menambahkan file lain (File2.cs) ke proyek yang terlihat seperti ini:

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

Pencarian kompilator Outer sebelum melihat mereka using arahan di luar namespace, sehingga ditemukan Outer.Math dari pada System.Math. Sayangnya (atau mungkin untungnya?), Outer.Math tidak punya PI anggota, jadi File1 sekarang rusak.

Ini berubah jika Anda memasukkan using di dalam deklarasi ruangnama Anda, sebagai berikut:

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

Sekarang pencarian kompilator System sebelum mencari Outer, menemukan System.Math, dan semuanya baik-baik saja.

Beberapa orang akan membantah hal itu Math mungkin nama yang buruk untuk kelas yang ditentukan pengguna, karena sudah ada satu System; intinya di sini hanya di sana aku s perbedaan, dan itu mempengaruhi pemeliharaan kode Anda.

Ini juga menarik untuk diperhatikan apa yang terjadi jika Foo dalam namespace Outer, daripada Outer.Inner. Dalam hal ini, menambahkan Outer.Math di File2 istirahat File1 di mana pun using pergi. Ini menyiratkan bahwa kompilator mencari ruang nama terlampir terdalam sebelum terlihat pada setiap using direktif.


1839
2017-09-30 02:33



Thread ini sudah memiliki beberapa jawaban yang bagus, tetapi saya merasa saya bisa memberikan sedikit lebih detail dengan jawaban tambahan ini.

Pertama, ingat bahwa deklarasi namespace dengan titik, seperti:

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

sepenuhnya setara dengan:

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

Jika Anda ingin, Anda bisa menempatkan using arahan pada semua level ini. (Tentu saja, kami ingin memilikinya usinghanya di satu tempat, tapi itu sah menurut bahasa.)

Aturan untuk menyelesaikan jenis yang tersirat, dapat dinyatakan secara bebas seperti ini: Pertama cari "lingkup" paling dalam untuk sebuah pertandingan, jika tidak ada yang ditemukan, pergilah satu tingkat ke ruang lingkup berikutnya dan cari di sana, dan seterusnya, hingga kecocokan ditemukan. Jika pada tingkat tertentu lebih dari satu kecocokan ditemukan, jika salah satu tipe berasal dari rakitan saat ini, pilih yang satu itu dan keluarkan peringatan kompiler. Jika tidak, menyerah (kesalahan waktu kompilasi).

Sekarang, mari kita eksplisit tentang apa artinya ini dalam contoh konkret dengan dua konvensi utama.

(1) Dengan penggunaan luar:

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;
    }
}

Dalam kasus di atas, untuk mengetahui tipe apa Ambiguous adalah, pencarian masuk dalam urutan ini:

  1. Jenis bersarang di dalam C (termasuk jenis bersarang yang diwariskan)
  2. Jenis dalam namespace saat ini MyCorp.TheProduct.SomeModule.Utilities
  3. Jenis dalam ruang nama MyCorp.TheProduct.SomeModule
  4. Jenis dalam MyCorp.TheProduct
  5. Jenis dalam MyCorp
  6. Jenis dalam batal namespace (namespace global)
  7. Jenis dalam System, System.Collections.Generic, System.Linq, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration, dan ThirdParty

Konvensi lainnya:

(2) Dengan penggunaan di dalam:

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;
    }
}

Sekarang, cari jenisnya Ambiguous masuk dalam urutan ini:

  1. Jenis bersarang di dalam C (termasuk jenis bersarang yang diwariskan)
  2. Jenis dalam namespace saat ini MyCorp.TheProduct.SomeModule.Utilities
  3. Jenis dalam System, System.Collections.Generic, System.Linq, MyCorp.TheProduct, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration, dan ThirdParty
  4. Jenis dalam ruang nama MyCorp.TheProduct.SomeModule
  5. Jenis dalam MyCorp
  6. Jenis dalam batal namespace (namespace global)

(Perhatikan itu MyCorp.TheProduct adalah bagian dari "3." dan karena itu tidak diperlukan antara "4." dan "5.".)

Pernyataan Penutup

Tidak masalah jika Anda menempatkan penggunaan di dalam atau di luar deklarasi namespace, selalu ada kemungkinan bahwa seseorang kemudian menambahkan tipe baru dengan nama identik ke salah satu ruang nama yang memiliki prioritas lebih tinggi.

Juga, jika namespace bersarang memiliki nama yang sama dengan tipe, itu dapat menyebabkan masalah.

Itu selalu berbahaya untuk memindahkan penggunaan dari satu lokasi ke lokasi lain karena hierarki pencarian berubah, dan jenis lainnya dapat ditemukan. Oleh karena itu, pilih satu konvensi dan patuhi itu, sehingga Anda tidak perlu memindahkannya.

Template Visual Studio, secara default, menempatkan penggunaannya di luar dari namespace (misalnya jika Anda membuat VS menghasilkan kelas baru dalam file baru).

Satu (kecil) keuntungan memiliki usings di luar adalah Anda kemudian dapat menggunakan arahan menggunakan untuk atribut global, misalnya [assembly: ComVisible(false)] dari pada [assembly: System.Runtime.InteropServices.ComVisible(false)].


345
2018-04-18 21:00



Menempatkannya di dalam ruang nama membuat deklarasi lokal ke ruang nama itu untuk file (jika Anda memiliki beberapa ruang nama dalam file) tetapi jika Anda hanya memiliki satu ruang nama per file maka itu tidak membuat banyak perbedaan apakah mereka pergi ke luar atau di dalam namespace.

using ThisNamespace.IsImported.InAllNamespaces.Here;

namespace Namespace1
{ 
   using ThisNamespace.IsImported.InNamespace1.AndNamespace2;

   namespace Namespace2
   { 
      using ThisNamespace.IsImported.InJustNamespace2;
   }       
}

namespace Namespace3
{ 
   using ThisNamespace.IsImported.InJustNamespace3;
}

178
2017-09-24 03:52



Menurut Hanselman - Menggunakan Directive dan Assembly Loading ... dan artikel lain semacam itu secara teknis tidak ada bedanya.

Preferensi saya adalah menempatkan mereka di luar ruang nama.


56
2017-09-24 03:53



Menurut Dokumentasi StyleCop:

SA1200: UsingDirectivesMustBePlacedWithinNamespace

Sebab Petunjuk penggunaan C # ditempatkan di luar elemen namespace.

Deskripsi Aturan Pelanggaran aturan ini terjadi ketika arahan menggunakan atau alias menggunakan-alias ditempatkan di luar elemen namespace, kecuali file tidak mengandung elemen namespace.

Misalnya, kode berikut akan menghasilkan dua pelanggaran aturan ini.

using System;
using Guid = System.Guid;

namespace Microsoft.Sample
{
    public class Program
    {
    }
}

Kode berikut, bagaimanapun, tidak akan menghasilkan pelanggaran aturan ini:

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

    public class Program
    {
    }
}

Kode ini akan dikompilasi dengan bersih, tanpa kesalahan kompilator. Namun, tidak jelas versi mana dari tipe Guid yang dialokasikan. Jika direktif yang digunakan dipindahkan di dalam namespace, seperti yang ditunjukkan di bawah ini, kesalahan kompilator akan terjadi:

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");
        }
    }
}

Kode gagal pada kesalahan kompiler berikut, ditemukan pada baris yang mengandung Guid g = new Guid("hello"); 

CS0576: Namespace 'Microsoft.Sample' berisi definisi yang bertentangan dengan alias 'Guid'

Kode menciptakan alias ke tipe System.Guid yang disebut Guid, dan juga membuat jenisnya sendiri yang disebut Guid dengan antarmuka konstruktor yang cocok. Kemudian, kode menciptakan turunan dari tipe Guid. Untuk membuat instance ini, compiler harus memilih antara dua definisi Guid yang berbeda. Ketika menggunakan-alias direktif ditempatkan di luar elemen namespace, compiler akan memilih definisi lokal Guid yang ditentukan dalam namespace lokal, dan sepenuhnya mengabaikan penggunaan-alias direktif yang ditentukan di luar ruang nama. Ini, sayangnya, tidak jelas ketika membaca kode.

Ketika menggunakan-alias direktif diposisikan dalam namespace, bagaimanapun, compiler harus memilih antara dua jenis Guid yang berbeda, yang saling bertentangan, keduanya didefinisikan dalam namespace yang sama. Kedua jenis ini memberikan konstruktor yang cocok. Compiler tidak dapat membuat keputusan, jadi ini menandakan kesalahan kompilator.

Menempatkan direktif menggunakan-alias di luar ruang nama adalah praktik yang buruk karena dapat menyebabkan kebingungan dalam situasi seperti ini, di mana tidak jelas versi jenis yang benar-benar digunakan. Ini berpotensi menyebabkan bug yang mungkin sulit didiagnosis.

Menempatkan menggunakan-alias arahan dalam elemen namespace menghilangkan ini sebagai sumber bug.

  1. Namespaces Berganda

Menempatkan beberapa elemen namespace dalam satu file umumnya merupakan ide yang buruk, tetapi jika dan ketika ini dilakukan, itu adalah ide yang baik untuk menempatkan semua menggunakan arahan dalam setiap elemen namespace, daripada secara global di bagian atas file. Ini akan mencakup ruang nama dengan erat, dan juga akan membantu menghindari jenis perilaku yang dijelaskan di atas.

Penting untuk dicatat bahwa ketika kode telah ditulis dengan menggunakan arahan yang ditempatkan di luar namespace, perhatian harus diambil ketika memindahkan arahan ini dalam namespace, untuk memastikan bahwa ini tidak mengubah semantik kode. Seperti yang dijelaskan di atas, menempatkan menggunakan-alias arahan dalam elemen namespace memungkinkan kompilator untuk memilih antara jenis yang bertentangan dengan cara yang tidak akan terjadi ketika arahan ditempatkan di luar ruang nama.

Cara Memperbaiki Pelanggaran Untuk memperbaiki pelanggaran aturan ini, pindahkan semua menggunakan arahan dan menggunakan-alias arahan dalam elemen namespace.


45
2017-09-14 15:17



Ada masalah dengan menempatkan menggunakan pernyataan di dalam namespace ketika Anda ingin menggunakan alias. Alias ​​tidak mendapat manfaat dari sebelumnya using pernyataan dan harus sepenuhnya memenuhi syarat.

Mempertimbangkan:

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

    class MyClass
    {
    }
}

melawan:

using System;

namespace MyNamespace
{
    using MyAlias = DateTime;

    class MyClass
    {
    }
}

Ini bisa sangat jelas jika Anda memiliki alias bertele-tele seperti berikut (yang bagaimana saya menemukan masalah):

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

Dengan using pernyataan di dalam namespace, tiba-tiba menjadi:

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

Tidak cantik.


29
2017-10-10 18:47



Seperti Jeppe Stig Nielsen kata, thread ini sudah memiliki jawaban yang bagus, tapi saya pikir ini cukup jelas juga patut disebutkan.

using arahan yang ditentukan di dalam ruang nama dapat dibuat untuk kode yang lebih pendek karena mereka tidak perlu sepenuhnya memenuhi syarat seperti ketika mereka ditentukan di luar.

Contoh berikut berfungsi karena jenisnya Foo dan Bar keduanya dalam namespace global yang sama, Outer.

Anggap file kode Foo.cs:

namespace Outer.Inner
{
    class Foo { }
}

Dan Bar.cs:

namespace Outer
{
    using Outer.Inner;

    class Bar
    {
        public Foo foo;
    }
}

Itu mungkin menghilangkan namespace luar di using direktif, singkatnya:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}

2
2017-09-17 10:32



Alasan teknisnya dibahas dalam jawaban dan saya pikir itu datang ke preferensi pribadi pada akhirnya karena perbedaannya bukan itu besar dan ada pengorbanan untuk keduanya. Template default Visual Studio untuk membuat .csfile digunakan using arahan di luar ruang nama mis. mis.

Satu dapat mengatur stylecop untuk memeriksa using arahan di luar ruang nama melalui penambahan stylecop.json file di root file proyek dengan yang berikut:

{
  "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
    "orderingRules": {
      "usingDirectivesPlacement": "outsideNamespace"
    }
  }
}

Anda dapat membuat file konfigurasi ini di tingkat solusi dan menambahkannya ke proyek Anda sebagai 'File Tautan yang Ada' untuk berbagi konfigurasi di semua proyek Anda juga.


0
2018-06-03 12:38



Ini adalah praktik yang lebih baik jika itu default menggunakan i.e. "referensi"digunakan dalam solusi sumber Anda harus berada di luar ruang nama dan yang ada "referensi tambahan baru" adalah praktik yang baik adalah Anda harus meletakkannya di dalam namespace. Ini untuk membedakan referensi apa yang ditambahkan.


-7
2017-10-14 21:30