การใช้คลาสสแตติกเป็นเนมสเปซ


21

ฉันเห็นผู้พัฒนารายอื่นใช้คลาสคงที่เป็นเนมสเปซ

public static class CategoryA
{
    public class Item1
    {
        public void DoSomething() { }
    }
    public class Item2
    {
        public void DoSomething() { }
    }
}

public static class CategoryB
{
    public class Item3
    {
        public void DoSomething() { }
    }
    public class Item4
    {
        public void DoSomething() { }
    }
}

เพื่อยกตัวอย่างชั้นเรียนภายในมันจะมีลักษณะดังต่อไปนี้

CategoryA.Item1 item = new CategoryA.Item1();

เหตุผลคือเนมสเปซสามารถซ่อนได้โดยใช้คีย์เวิร์ด "using" แต่โดยการใช้คลาสคงที่ชื่อคลาสเลเยอร์ด้านนอกจะต้องระบุซึ่งจะรักษาเนมสเปซได้อย่างมีประสิทธิภาพ

Microsoft ให้คำแนะนำในแนวทางนั้น โดยส่วนตัวฉันคิดว่ามันส่งผลกระทบต่อความสามารถในการอ่าน คุณคิดยังไง?


1
มันขึ้นอยู่กับผู้ใช้งานว่าพวกเขาต้องการเนมสเปซอยู่ในการติดตั้งหรือไม่ ทำไมคุณต้องบังคับให้ใช้เนมสเปซ (และเนมสเปซปลอม)
Craige

อาจมีวัตถุประสงค์เพื่อจัดกลุ่ม? เหมือนมีคลาสย่อยจำนวนมากและโดยการจัดกลุ่มคลาสย่อยเป็นคลาสสแตติก นั่นทำให้ความตั้งใจชัดเจน
user394128

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

คำตอบ:


16

การใช้คลาสสแตติกเป็นเนมสเปซท้าทายวัตถุประสงค์ของการมีเนมสเปซ

ความแตกต่างที่สำคัญอยู่ที่นี่:

หากคุณกำหนดCategoryA, CategoryB<เป็น namespaces และเมื่อสมัครใช้สอง namespaces:

CategoryA::Item1 item = new CategoryA::Item1(); 
CategoryB::Item1 item = new CategoryB::Item1();

ที่นี่ถ้าCategoryAหรือCategoryBเป็นคลาสแบบคงที่มากกว่าเนมสเปซการใช้งานสำหรับแอปพลิเคชันเกือบจะเหมือนกับที่อธิบายไว้ข้างต้น

อย่างไรก็ตามหากคุณกำหนดเป็นเนมสเปซและแอปพลิเคชันจะใช้เพียง 1 เนมสเปซ (ไม่รวมCategoryB) ในกรณีที่แอปพลิเคชันนั้นสามารถใช้งานได้จริงดังต่อไปนี้

using namespace CategoryA; 
Item1 item = new Item1(); 

แต่ถ้าคุณนิยามCategoryAเป็นคลาสสแตติกข้างต้นจะไม่ได้กำหนด! หนึ่งถูกบังคับให้เขียนCategoryA.somethingทุกครั้ง

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


นอกจากนี้หากมีคนต้องการที่จะไม่ใช้เนมสเปซแม้กระทั่งการใช้คลาสสามารถ: using Item1 = CategoryA.Item1(ฉันคิดว่ามันใช้งานได้สำหรับคลาสที่ซ้อนกันเช่นเดียวกับเนมสเปซ)
George Duckett

1
ใน C ++ ADLไม่ทำงานกับขอบเขตของคลาส มันทำงานกับขอบเขตเนมสเปซ ดังนั้นใน C ++ namespace ไม่ได้เป็นเพียงทางเลือกธรรมชาติ แต่ที่ถูกต้องและสำนวนทางเลือกที่ยัง
นาวาซ

ฉันคิดว่ามันเป็นความตั้งใจของ OP ที่จะหลีกเลี่ยงการซ่อนเนมสเปซโดยใช้คำหลัก เขาต้องการบังคับให้ผู้ใช้เขียนทุกครั้ง บางทีเนมสเปซของเขาก็เหมือนvar s = new HideMe.MyPhoneNumberIs12345678.TheRealUsefulClass()
Gqqnbig

5

สิ่งที่เกิดขึ้นที่นี่แน่นอนไม่ได้เป็นรหัส C #

บางทีคนอื่น ๆ เหล่านี้พยายามที่จะใช้รูปแบบของโมดูล - สิ่งนี้จะช่วยให้คุณมีฟังก์ชั่นการกระทำและอื่น ๆ รวมทั้งคลาสใน 'เนมสเปซ' (เช่นคลาสแบบสแตติกในกรณีนี้)


ฉันเห็นด้วยดูแปลก ๆ
marko

4

ประการแรกทุกคลาสควรอยู่ในเนมสเปซดังนั้นตัวอย่างของคุณจะเป็นดังนี้:

SomeNamespace.CategoryA.Item1 item = new SomeNamespace.CategoryA.Item1();

ที่กล่าวว่าฉันไม่เห็นประโยชน์ของประเภทยิมนาสติกรหัสเมื่อคุณสามารถกำหนด namespace เช่นนี้ได้:

namespace SomeNamespace.CategoryA { ... }

หากคุณอาจคิดว่าการเก็บวิธีคงที่บางวันในระดับบนอาจทำให้รู้สึก แต่ก็ไม่ใช่สิ่งที่ผู้สร้าง C # มีในใจและอาจทำให้คนอื่นอ่านน้อยลง - ฉันจะเสียเวลามาก กำลังคิดว่าทำไมคุณทำสิ่งนี้และคิดว่าฉันทำแฟ้มต้นฉบับบางไฟล์ที่จะให้คำอธิบาย


ดังนั้นทำไม enums ข้อยกเว้น ?
sq33G

นั่นเป็นคำถามที่แตกต่างกัน :) แต่ฉันยังคงไม่ได้กำหนดคลาสแบบสแตติกเพื่อที่จะกำหนด enums ไว้
zmilojko

1

เนมสเปซใน Java, JavaScript และ. NET ที่เกี่ยวข้องยังไม่เสร็จสมบูรณ์และอนุญาตให้เก็บคลาสเท่านั้น แต่สิ่งอื่น ๆ เช่นค่าคงที่หรือวิธีการสากลไม่ควรทำ

นักพัฒนาหลายคนใช้เทคนิค "คลาสคงที่" หรือ "วิธีการคงที่" แม้ว่าบางคนไม่แนะนำ


0

ฉันรู้ว่านี่เป็นคำถามเก่า แต่เหตุผลหนึ่งที่ถูกต้องมากที่จะใช้คลาสเป็น namespace (ไม่ใช่แบบคงที่ที่) คือ C # ไม่สนับสนุนความหมายของ namespaces พารามิเตอร์หรือทั่วไป ฉันเขียนโพสต์บล็อกในหัวข้อนี้มากที่นี่: http://tyreejackson.com/generics-net-part5-generic-namespaces/http://tyreejackson.com/generics-net-part5-generic-namespaces/

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

การใช้คลาส Generic ภายนอกและการซ้อนประเภทที่เกี่ยวข้องภายในสามารถทำให้โค้ดแห้งและมีความเป็นนามธรรมได้ง่ายขึ้น หนึ่งสามารถรับคลาสเนมสเปซพาราเมตริกด้วยการใช้งานที่เป็นรูปธรรมซึ่งให้รายละเอียดที่เป็นรูปธรรมทั้งหมด

นี่คือตัวอย่างเล็กน้อย:

public  class   Entity
                <
                    TEntity, 
                    TDataObject, 
                    TDataObjectList, 
                    TIBusiness, 
                    TIDataAccess, 
                    TIdKey
                >
        where   TEntity         : Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TIdKey>, subclassed
        where   TDataObject     : Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TIdKey>.BaseDataObject, subclassed
        where   TDataObjectList : Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TIdKey>.BaseDataObjectList, subclassed
        where   TIBusiness      : Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TIdKey>.IBaseBusiness
        where   TIDataAccess    : Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TIdKey>.IBaseDataAccess
{

    public class    BaseDataObject
    {
        public TIdKey Id { get; set; }
    }

    public class BaseDataObjectList : Collection<TDataObject> {}

    public interface IBaseBusiness
    {

        TDataObject     LoadById(TIdKey id);
        TDataObjectList LoadAll();
        void            Save(TDataObject item);
        void            Save(TDataObjectList items);
        void            DeleteById(TIdKey id);
        bool            Validate(TDataObject item);
        bool            Validate(TDataObjectList items);

    }

    public interface IBaseDataAccess
    {

        TDataObject     LoadById(TIdKey id);
        TDataObjectList LoadAll();
        void            Save(TDataObject item);
        void            Save(TDataObjectList items);
        void            DeleteById(TIdKey id);

    }

}

ใช้แบบนี้:

public  class   User 
:
                Entity
                <
                    User, 
                    User.DataObject, 
                    User.DataObjectList, 
                    User.IBusiness, 
                    User.IDataAccess, 
                    Guid
                >
{
    public class DataObject : BaseDataObject
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

    public class DataObjectList : BaseDataObjectList {}

    public interface IBusiness : IBaseBusiness
    {
        void DeactivateUserById(Guid id);
    }

    public interface IDataAcccess : IBaseDataAccess {}
}

บริโภคตราสารอนุพันธ์เช่นนี้:

public class EntityConsumer
{
    private User.IBusiness       userBusiness;
    private Permission.IBusiness permissionBusiness;

    public EntityConsumer(User.IBusiness userBusiness, Permission.IBusiness permissionBusiness) { /* assign dependencies */ }

    public void ConsumeEntities()
    {
        var users       = new User.DataObjectList();
        var permissions = this.permissionBusiness.LoadAll();

        users.Add
        (new User.DataObject()
        {
            // Assign property values
        });

        this.userBusiness.Save(users);

    }
}

ประโยชน์ในการเขียนประเภทด้วยวิธีนี้คือเพิ่มความปลอดภัยของประเภทและการคัดเลือกประเภทในคลาสนามธรรมน้อยลง มันเทียบArrayListกับList<T>ในขนาดที่ใหญ่กว่า

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