เนมสเปซและคลาสที่มีชื่อเดียวกัน?


102

ฉันกำลังจัดโปรเจ็กต์ห้องสมุดและฉันมีชื่อคลาสผู้จัดการส่วนกลางScenegraphและคลาสอื่น ๆ ทั้งหมดที่อาศัยอยู่ในเนมสเปซของ Scenegraph

สิ่งที่ฉันต้องการจริงๆคือให้ฉากกราฟเป็นMyLib.Scenegraphและคลาสอื่น ๆMyLib.Scenegraph.*แต่ดูเหมือนว่าวิธีเดียวที่จะทำได้คือสร้างคลาสอื่น ๆ ทั้งหมดภายในคลาสScenegraphในไฟล์ Scenegraph.cs และนั่นก็เทอะทะเกินไป .

แต่ฉันได้จัดระเบียบเป็นMylib.Scenegraph.ScenegraphและMyLib.Scenegraph.*ประเภทของงาน แต่ฉันพบว่า Visual Studio สับสนภายใต้เงื่อนไขบางประการว่าฉันกำลังอ้างถึงคลาสหรือเนมสเปซ

มีวิธีที่ดีไหมในการจัดระเบียบแพ็กเกจนี้เพื่อให้สะดวกสำหรับผู้ใช้โดยไม่ต้องรวมโค้ดทั้งหมดเข้าด้วยกันอย่างไม่เป็นระเบียบ?

คำตอบ:


112

ผมไม่แนะนำให้คุณตั้งชื่อชั้นเช่น namespace ของมันให้ดูบทความนี้

แนวทางการออกแบบกรอบกล่าวไว้ในส่วน 3.4“ ห้ามใช้ชื่อเดียวกันสำหรับเนมสเปซและประเภทในเนมสเปซนั้น” นั่นคือ:

namespace MyContainers.List 
{ 
    public class List { … } 
}

ทำไมความเลวนี้ โอ้ให้ฉันนับวิธี

คุณอาจตกอยู่ในสถานการณ์ที่คุณคิดว่าคุณกำลังอ้างถึงสิ่งหนึ่ง แต่ที่จริงแล้วกำลังอ้างถึงสิ่งอื่น สมมติว่าคุณตกอยู่ในสถานการณ์ที่โชคร้ายนี้: คุณกำลังเขียน Blah.DLL และนำเข้า Foo.DLL และ Bar.DLL ซึ่งน่าเสียดายที่ทั้งคู่มีประเภทที่เรียกว่า Foo:

// Foo.DLL: 
namespace Foo { public class Foo { } }

// Bar.DLL: 
namespace Bar { public class Foo { } }

// Blah.DLL: 
namespace Blah  
{   
using Foo;   
using Bar;   
class C { Foo foo; } 
}

คอมไพเลอร์แสดงข้อผิดพลาด “ Foo” มีความคลุมเครือระหว่าง Foo.Foo และ Bar.Foo แย่จัง. ฉันเดาว่าฉันจะแก้ไขโดยกำหนดชื่อให้ครบถ้วน:

   class C { Foo.Foo foo; } 

ตอนนี้ทำให้เกิดข้อผิดพลาดที่ไม่ชัดเจน“ Foo in Foo Foo มีความคลุมเครือระหว่าง Foo.Foo และ Bar.Foo ” เรายังไม่รู้ว่า Foo ตัวแรกหมายถึงอะไรและจนกว่าเราจะเข้าใจได้เราก็ไม่ต้องกังวลที่จะพยายามหาว่าอันที่สองหมายถึงอะไร


8
มันเป็นจุดที่น่าสนใจ ฉันยังคงคิดถึงมัน ขอบคุณ. ฉันต้องเป็นข้อโต้แย้งอย่างจริงใจที่เริ่มต้นด้วย "ไกด์บอกว่า" อย่าทำให้ฉันประทับใจ ฉันเคยเห็นคำแนะนำสไตล์อึที่ไม่ยุติธรรมมากมาย แต่คุณโต้แย้งในทางปฏิบัติที่ดีข้างต้น คุ้มที่จะคิดต่อไป
user430788

6
คำตอบระบุว่า "ไม่ต้องทำอะไร" ในขณะที่ผู้ใช้สแต็กบางคนมองหา "สิ่งที่ต้องทำ" มีใครแนะนำ Ant_222 ตอบหน่อยครับ
fantastory

3
หากคุณติดขัดจริงๆคุณยังคงสามารถสร้างนามแฝงประเภทนอกเนมสเปซของคุณได้ นามแฝงภายนอกจำเป็นต่อเมื่อคุณมีตัวระบุเต็มสองตัวที่เหมือนกันทั้งหมด (เนมสเปซ + ชื่อคลาส)
Geoffrey

4
ทั้งหมดข้างต้นเป็นผลพลอยได้และความเห็น ความเป็นจริงคือการที่คุณไม่ควรทำเพราะเรียบเรียง C # จะเข้าใจผิดมันแม้จะได้รับการบอกค่อนข้างชัดเจนสิ่งที่เกิดขึ้น เช่นusing Foo.Bar;นั้นก็Bar bจะค่อนข้างชัดเจนหมายถึงBarคลาส (หรือ enum) ภายในการ Foo.Barnamespace เหตุผลที่แท้จริงที่จะไม่ทำเช่นนั้นก็คือคอมไพเลอร์ C # ไม่สามารถเข้าใจได้อย่างถูกต้องซึ่งเป็นเรื่องที่น่าแปลกใจและโชคร้ายอย่างมาก อย่างอื่นคือคำแนะนำสไตล์ขนสัตว์
TJ Crowder

2
ฉันไม่เข้าใจ ทำไม Foo Foo จึงมีความคลุมเครือ? คุณพูดกับคอมไพเลอร์โดยตรงว่าคุณต้องการใช้คลาส Foo จากเนมสเปซ Foo ในกรณีเฉพาะนี้ ไม่?
GuardianX

15

การตั้งชื่อเดียวกันให้กับเนมสเปซและคลาสอาจทำให้คอมไพเลอร์สับสนได้ตามที่คนอื่นพูด

แล้วจะตั้งชื่อยังไง?

หากเนมสเปซมีหลายคลาสให้ค้นหาชื่อที่กำหนดคลาสเหล่านั้นทั้งหมด

ถ้าnamespace มีเพียงหนึ่งชั้น (และด้วยเหตุนี้สิ่งล่อใจที่จะให้มันชื่อเดียวกัน) ชื่อ namespace ClassName NS นี่คือวิธีที่ Microsoft ตั้งชื่อเนมสเปซอย่างน้อย


6
คุณมีตัวอย่างของเนมสเปซจาก Microsoft หรือไม่?
อาทิตย์

@sunefred ฉันค้นหา แต่ไม่พบ แต่ฉันจำได้ว่าเคยเห็นในเอกสาร ฉันเดาว่ามีไม่มากนักที่คุณต้องการมีคลาสเดียวในเนมสเปซ
GoTo

9

ผมขอแนะนำว่าคุณทำตามคำแนะนำที่ผมได้รับในmicrosoft.public.dotnet.languages.csharpการใช้งานและMyLib.ScenegraphUtil.ScenegraphMyLib.ScenegraphUtil.*


7

CA1724: Type Names Should Not Match Namespaces ...

โดยทั่วไปหากคุณทำตาม Code Analysis เพื่อการเข้ารหัสที่เหมาะสมกฎนี้จะบอกว่าอย่าทำในสิ่งที่คุณพยายามจะทำ การวิเคราะห์โค้ดมีประโยชน์มากในการช่วยคุณค้นหาปัญหาที่อาจเกิดขึ้น


5

แม้ว่าฉันจะเห็นด้วยกับคำตอบอื่น ๆ ที่คุณไม่ควรตั้งชื่อชั้นเรียนเหมือนกับเนมสเปซของคุณแต่ก็มีบางครั้งที่คุณไม่สามารถปฏิบัติตามข้อกำหนดดังกล่าวได้

ในกรณีของฉันเช่นฉันไม่ใช่คนที่ตัดสินใจเช่นนั้นดังนั้นฉันจึงต้องหาวิธีที่จะทำให้มันได้ผล

ดังนั้นสำหรับผู้ที่ไม่สามารถเปลี่ยนชื่อเนมสเปซหรือชื่อคลาสได้นี่คือวิธีที่คุณสามารถทำให้โค้ดของคุณทำงานได้

// Foo.DLL: 
namespace Foo { public class Foo { } }

// Bar.DLL: 
namespace Bar { public class Foo { } }

// Blah.DLL: 
namespace Blah
{
    using FooNSAlias = Foo;//alias
    using BarNSAlias = Bar;//alias
    class C { FooNSAlias.Foo foo; }//use alias to fully qualify class name
}

โดยพื้นฐานแล้วฉันสร้างเนมสเปซ "นามแฝง" และนั่นทำให้ฉันมีคุณสมบัติครบถ้วนสำหรับคลาสและ "ความสับสน" ของ Visual Studio ก็หายไป

หมายเหตุ: คุณควรหลีกเลี่ยงความขัดแย้งในการตั้งชื่อนี้หากอยู่ในการควบคุมของคุณที่จะทำเช่นนั้น คุณควรใช้เทคนิคที่กล่าวถึงก็ต่อเมื่อคุณไม่ได้ควบคุมคลาสและเนมสเปซที่เป็นปัญหา


2
ฉันโหวตให้เพราะมันเป็นทางออกที่น่าสนใจสำหรับโลกที่ไม่สมบูรณ์แบบ
user430788

4

เพียงแค่เพิ่ม 2 เซ็นต์ของฉัน:

ฉันมีชั้นเรียนต่อไปนี้:

namespace Foo {
    public struct Bar {
    }
    public class Foo {
        //no method or member named "Bar"
    }
}

ลูกค้าเขียนแบบนี้:

using Foo;

public class Blah {
    public void GetFoo( out Foo.Bar[] barArray ) {
    }
}

การให้อภัยข้อผิดพลาด GetFoo ไม่ส่งคืนเอาต์พุตแทนที่จะใช้พารามิเตอร์ out คอมไพลเลอร์ไม่สามารถแก้ไขชนิดข้อมูล Foo.Bar [] มันส่งคืนข้อผิดพลาด: ไม่พบประเภทหรือเนมสเปซ Foo.Bar

ปรากฏว่าเมื่อพยายามคอมไพล์ก็แก้ไข Foo เป็นคลาสและไม่พบแถบคลาสแบบฝังในคลาส Foo นอกจากนี้ยังไม่พบเนมสเปซชื่อ Foo.Bar ค้นหาแถบคลาสในเนมสเปซ Foo ไม่สำเร็จ จุดในช่องชื่อไม่ใช่รูปแบบไวยากรณ์ สตริงทั้งหมดเป็นโทเค็นไม่ใช่คำที่คั่นด้วยจุด

ลักษณะการทำงานนี้แสดงโดย VS 2015 ที่รัน. Net 4.6


3

ตามที่คนอื่น ๆ กล่าวไว้เป็นแนวทางปฏิบัติที่ดีในการหลีกเลี่ยงการตั้งชื่อคลาสให้เหมือนกับเนมสเปซ

ต่อไปนี้เป็นคำแนะนำในการตั้งชื่อเพิ่มเติมจากคำตอบโดย svickไปยังคำถามที่เกี่ยวข้อง "คลาสเดียวกันและชื่อเนมสเปซ" ใน Software Engineering Stack Exchange:

คุณคิดถูกแล้วที่ไม่ควรตั้งชื่อเนมสเปซเหมือนกับประเภทที่มีอยู่ ฉันคิดว่ามีหลายวิธีที่คุณสามารถใช้ได้:

  • พหูพจน์: Model.DataSources.DataSource

สิ่งนี้ใช้ได้ดีโดยเฉพาะอย่างยิ่งหากจุดประสงค์หลักของเนมสเปซคือการมีประเภทที่สืบทอดมาจากประเภทพื้นฐานเดียวกันหรือใช้อินเทอร์เฟซเดียวกัน

  • ย่อ: Model.QueryStorage

หากเนมสเปซมีเพียงไม่กี่ประเภทคุณอาจไม่จำเป็นต้องใช้เนมสเปซนั้นเลย

  • สร้างองค์กร: Model.ProjectSystem.Project

สิ่งนี้สามารถทำงานได้โดยเฉพาะอย่างยิ่งกับคุณลักษณะที่เป็นส่วนสำคัญของผลิตภัณฑ์ของคุณดังนั้นจึงสมควรได้รับชื่อของตัวเอง

(หมายเหตุว่าข้างต้นคำตอบการใช้งานModel.DataSource.DataSource, Model.QueryStorage.QueryStorage.และModel.Project.Projectเป็นตัวอย่างมากกว่าMyLib.Scenegraph.Scenegraph.)

(ฉันยังพบคำแนะนำในการตั้งชื่ออื่น ๆ ในคำตอบอื่น ๆ ที่นี่มีประโยชน์)


2

โพสต์เก่า แต่ที่นี่ฉันไปกับแนวคิดอื่นที่อาจช่วยใครบางคน:

"... แต่ดูเหมือนวิธีเดียวที่จะทำได้คือทำให้คลาสอื่น ๆ ภายในคลาสของ Scenegraph ในไฟล์ Scenegraph.cs นั้นเทอะทะเกินไป"

นี่เป็นการนำไปใช้งานที่ดีกว่าสำหรับสถานการณ์ต่างๆ แต่ฉันยอมรับว่าการมีโค้ดทั้งหมดในไฟล์. cs เดียวกันนั้นน่ารำคาญ (พูดน้อยที่สุด)

คุณสามารถแก้ปัญหาได้โดยทำให้คลาสพื้นฐานเป็น "คลาสบางส่วน" จากนั้นไปที่การสร้างคลาสภายในในไฟล์ของตัวเอง (โปรดจำไว้ว่าพวกเขาจะต้องประกาศส่วนเสริมคลาสพื้นฐานจากนั้นไปที่คลาสด้านในเฉพาะ สำหรับไฟล์นั้น)

สิ่งที่ต้องการ...

Scenegraph.cs:

namespace MyLib
{
    public partial class Scenegraph
    {
        //Scenegraph specific implementations
    }
}

DependentClass.cs:

namespace MyLib
{
    public partial class Scenegraph
    {
        public class DependentClass
        {
            //DependentClass specific implementations
        }
    }
}

ฉันคิดว่านี่เป็นสิ่งที่ใกล้ชิดมากขึ้นที่คุณจะสามารถใช้งานคลาสภายในได้อย่างหมดจดในขณะที่ไม่ต้องยุ่งทุกอย่างภายในไฟล์ขนาดใหญ่และยุ่งเหยิง


2

เกิดขึ้นเมื่อเป็นคลาสหลักของเนมสเปซ ดังนั้นจึงเป็นแรงจูงใจอย่างหนึ่งในการใส่เนมสเปซในไลบรารีปัญหาจะหายไปหากคุณเพิ่ม 'Lib' ในชื่อเนมสเปซ ...

namespace SocketLib
{
    class Socket
    {

น้ำยาทำความสะอาด. ฉันใช้จุดเหมือนLib.Socketและพบปัญหาเมื่อชื่อคลาสเหมือนกัน ตอนนี้ใช้จุดน้อยลง
robsn
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.