Enum“ มรดก”


391

ฉันมี enum ในเนมสเปซระดับต่ำ ฉันต้องการให้คลาสหรือ enum ในเนมสเปซระดับกลางที่ "สืบทอด" enum ระดับต่ำ

namespace low
{
   public enum base
   {
      x, y, z
   }
}

namespace mid
{
   public enum consume : low.base
   {
   }
}

ฉันหวังว่าสิ่งนี้จะเป็นไปได้หรือบางทีคลาสบางประเภทที่สามารถใช้แทน enum ที่จะให้สิ่งที่เป็นนามธรรมสำหรับ enum แต่ยังให้อินสแตนซ์ของคลาสนั้นเข้าถึง enum ได้

คิด?

แก้ไข: หนึ่งในเหตุผลที่ฉันไม่ได้เพียงแค่เปลี่ยนสิ่งนี้เป็น const ในคลาสก็คือต้องการบริการ enum ระดับต่ำที่ฉันต้องใช้ ฉันได้รับ WSDLs และ XSD ซึ่งกำหนดโครงสร้างเป็น enum ไม่สามารถเปลี่ยนบริการได้


1
ทางเลือกหนึ่งคือcodeproject.com/Articles/20805/Enhancing-C-Enums
Walter Vehoeven

คำตอบ:


462

สิ่งนี้เป็นไปไม่ได้ Enums ไม่สามารถสืบทอดจาก enums อื่น ๆ ในความเป็นจริง enums System.Enumทุกคนจะต้องได้รับมรดกจริงจาก C # อนุญาตให้ไวยากรณ์เปลี่ยนการแสดงพื้นฐานของค่า enum ซึ่งดูเหมือนว่าสืบทอด แต่ในความเป็นจริงพวกเขายังคงสืบทอดมาจาก System.enum

ดูส่วน 8.5.2 ของข้อมูลจำเพาะ CLIสำหรับรายละเอียดแบบเต็ม ข้อมูลที่เกี่ยวข้องจากข้อมูลจำเพาะ

  • enums ทั้งหมดจะต้องได้รับจาก System.Enum
  • เนื่องจากด้านบน enums ทั้งหมดเป็นชนิดค่าและดังนั้นจึงถูกผนึก

2
และประเภทค่าทั้งหมดได้รับมาจาก System.ValueType
Raz Megrelidze

4
ต้องพูดถึงว่าคำตอบของ @Seven เป็นวิธีแก้ปัญหาที่ถูกต้องตามกฎหมาย: stackoverflow.com/a/4042826/538387
Tohid

แต่คำตอบของ @ Steven ไม่สามารถใช้ได้ในswitchสถานการณ์นี้
zionpi

@zionpi ใช่ แต่โปรดทราบว่า (ฉันเชื่อว่า) สวิตช์มาตรฐานได้รับการรวบรวมเป็นรหัส IL เดียวกันเต็มถ้าอื่นถ้าบล็อกอื่นจะ: ซึ่งฉันคิดว่ามีไวยากรณ์ที่ดีกว่าดีกว่าอยู่แล้ว คุณมีความสามารถที่หลวมสำหรับ resharper / VS ในการเติมข้อความทั้งหมดให้สมบูรณ์โดยอัตโนมัติ แต่ฉันคิดว่านั่นไม่ใช่จุดสิ้นสุดของโลก มันเป็นความชอบส่วนตัว แต่ฉันไม่ใช่แฟนของคำสั่ง switch
MemeDeveloper

165

คุณสามารถบรรลุสิ่งที่คุณต้องการในชั้นเรียน:

public class Base
{
    public const int A = 1;
    public const int B = 2;
    public const int C = 3;
}
public class Consume : Base
{
    public const int D = 4;
    public const int E = 5;
}

ตอนนี้คุณสามารถใช้คลาสเหล่านี้คล้ายกับตอนที่ enums:

int i = Consume.B;

อัปเดต (หลังจากอัปเดตคำถามของคุณ):

หากคุณกำหนดค่า int เดียวกันให้กับค่าคงที่ตามที่กำหนดไว้ใน enum ที่มีอยู่คุณสามารถใช้ระหว่าง enum และค่าคงที่เช่น:

public enum SomeEnum // this is the existing enum (from WSDL)
{
    A = 1,
    B = 2,
    ...
}
public class Base
{
    public const int A = (int)SomeEnum.A;
    //...
}
public class Consume : Base
{
    public const int D = 4;
    public const int E = 5;
}

// where you have to use the enum, use a cast:
SomeEnum e = (SomeEnum)Consume.B;

8
คุณจะระบุฟิลด์ในคลาสนี้ได้อย่างไร? สำหรับฉันนั่นเป็นพฤติกรรมที่สำคัญของ enum:Enum.GetValues(typeof(MyEnum)
Mike de Klerk

1
คุณสามารถใช้การสะท้อน: void Test() { foreach (System.Reflection.PropertyInfo pi in typeof(Consume).GetProperties()) { Console.WriteLine(pi.Name); } }
mnieto

2
คุณต้องตรวจสอบให้แน่ใจว่าการรวบรวมคุณสมบัติที่มีการสะท้อนนั้นไม่สนใจคุณสมบัติวัตถุที่สืบทอดมา
Robert

Reflection นั้นน่าเกลียดมากสำหรับงานนั้น
dylanh724

1
ไม่จำเป็นต้องใช้ Reflection การใช้งานที่ codeproject.com/Articles/20805/Enhancing-C-Enums เป็นวิธีที่ดีในการทำเช่นนี้เนื่องจากวัตถุที่ถูกสร้างขึ้นจะถูกเพิ่มลงในรายการและสามารถใช้รายการนั้นเพื่อ ส่งคืนรายการชนิดของวัตถุ เมื่อคุณผสมกับการสืบทอดคุณจะต้องตรวจสอบให้แน่ใจว่าคุณกำลังใช้รายการที่ถูกต้องสำหรับคลาสการสืบทอด
PBo

112

คำตอบสั้น ๆ คือไม่ คุณสามารถเล่นได้เล็กน้อยถ้าคุณต้องการ:

คุณสามารถทำสิ่งนี้ได้ตลอดเวลา:

private enum Base
{
    A,
    B,
    C
}

private enum Consume
{
    A = Base.A,
    B = Base.B,
    C = Base.C,
    D,
    E
}

แต่มันใช้งานไม่ได้ดีนักเพราะ Base.A! = Consume.A

คุณสามารถทำสิ่งนี้ได้ตลอดเวลาแม้ว่า:

public static class Extensions
{
    public static T As<T>(this Consume c) where T : struct
    {
        return (T)System.Enum.Parse(typeof(T), c.ToString(), false);
    }
}

เพื่อข้ามระหว่างฐานและใช้ ...

คุณยังสามารถใช้ค่าของ enums เป็น ints และเปรียบเทียบกับ ints แทนที่จะเป็น enum แต่การดูดนั้นก็เช่นกัน

การคืนเมธอดส่วนขยายควรพิมพ์ cast it type T


4
ฉันขุดคนนี้ ใช้แนวคิดนี้เพื่อทำให้เป็นฟอง enums จาก ORM ของฉันไปยังส่วนต่อประสานสาธารณะของฉัน (กับที่ไม่มีการอ้างอิง ORM)
ObjectType

5
หนึ่งสามารถใช้ enums เพื่อเปรียบเทียบกับการทำงาน:Base.A == (Base)Consume.A
Kresimir

1
ใช้ (ฐานสิบ) Base.A == (ฐานสิบ) ใช้ เหตุผล: นี่คือการรวมบิตแฟล็ก / มาสก์ (ตัวอย่างเช่นใน Enum.IsDefined msdn.microsoft.com/en-us/library/… ) ดังนั้น enum สามารถตั้งค่าเป็นจำนวนเต็มไม่ได้กำหนดไว้ใน enum กินการทดสอบ = 123456;
TamusJRoyce

3
@TamusJRoyce ทศนิยม? intจะทำให้รู้สึกมากขึ้น เมื่อ Enum จะมีส่วนที่เป็นเศษส่วน!?!?!
ErikE

อย่างน้อยที่สุดนี่ทำให้มั่นใจได้ว่าค่าคงที่ enum ที่สอดคล้องกันมีค่าจำนวนเต็มเหมือนกัน หลังจากทั้งหมด enums เป็นเพียงชุดของค่าคงที่จำนวนเต็ม C # ไม่บังคับใช้ว่าค่าที่กำหนดเป็นค่าคงที่ vaild enum เช่น enums ไม่ได้พิมพ์อย่างปลอดภัยในแง่ที่เข้มงวด
Olivier Jacot-Descombes

98

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

คุณจะต้องเขียน

public void DoSomethingMeaningFull(int consumeValue) ...

อย่างไรก็ตามมีวิธีแก้ปัญหาตามคลาสของวันเก่าของ Java เมื่อไม่มี enums พร้อมใช้งาน สิ่งนี้ให้พฤติกรรมที่เกือบเหมือน enum ข้อแม้เดียวคือค่าคงที่เหล่านี้ไม่สามารถใช้ภายในคำสั่งสวิตช์

public class MyBaseEnum
{
    public static readonly MyBaseEnum A = new MyBaseEnum( 1 );
    public static readonly MyBaseEnum B = new MyBaseEnum( 2 );
    public static readonly MyBaseEnum C = new MyBaseEnum( 3 );

    public int InternalValue { get; protected set; }

    protected MyBaseEnum( int internalValue )
    {
        this.InternalValue = internalValue;
    }
}

public class MyEnum : MyBaseEnum
{
    public static readonly MyEnum D = new MyEnum( 4 );
    public static readonly MyEnum E = new MyEnum( 5 );

    protected MyEnum( int internalValue ) : base( internalValue )
    {
        // Nothing
    }
}

[TestMethod]
public void EnumTest()
{
    this.DoSomethingMeaningful( MyEnum.A );
}

private void DoSomethingMeaningful( MyBaseEnum enumValue )
{
    // ...
    if( enumValue == MyEnum.A ) { /* ... */ }
    else if (enumValue == MyEnum.B) { /* ... */ }
    // ...
}

7
ฉันคิดว่านี่เป็นคำตอบที่ถูก คุณไม่สามารถรับมรดกสำหรับ Enum ได้ แต่สิ่งนี้จะช่วยให้คุณจัดการได้!
robob

11
สวยและสะอาด +1 เป็นเพียงคำใบ้คุณไม่จำเป็นต้องมีค่า int เลย
Ignacio Soler Garcia

1
ฉันไม่เคยคิดเกี่ยวกับการแจกแจงราวกับว่าคุณสามารถพูด FileOpenMode.Read> FileOpenMode.Write หากเป็นเช่นนั้นคุณจำเป็นต้องมี int หรือดีกว่า IEqualityComparer สำหรับ enum นั้น ความนับถือ.
Ignacio Soler Garcia

3
@binki โดยใช้ random object's จะทำให้ค่าแตกต่างกันสำหรับแต่ละ instantiation ของการชุมนุมเพื่อให้พวกเขาไม่สามารถต่อเนื่องกัน
ivan_pozdeev

2
แต่สิ่งนี้ไม่อนุญาตให้คุณ "เปลี่ยน" บน MyEnum ใช่ไหม? ฉันหมายถึงส่วนใหญ่แล้วทำไมฉันถึงใช้ enums ...
MemphiZ

13

ไม่สนใจข้อเท็จจริงที่ว่าฐานนั้นเป็นคำที่สงวนไว้ซึ่งคุณไม่สามารถรับมรดกของ enum ได้

สิ่งที่ดีที่สุดที่คุณสามารถทำได้คือสิ่งที่:

public enum Baseenum
{
   x, y, z
}

public enum Consume
{
   x = Baseenum.x,
   y = Baseenum.y,
   z = Baseenum.z
}

public void Test()
{
   Baseenum a = Baseenum.x;
   Consume newA = (Consume) a;

   if ((Int32) a == (Int32) newA)
   {
   MessageBox.Show(newA.ToString());
   }
}

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


2
สงวนไว้ แต่ไม่ได้เป็นฐาน
erikkallen

2
เขาหมายถึงการใช้ OP ของฐานเป็นชื่อ enum ก็เป็นเพียงชื่อตัวอย่างเช่นฉันแน่ใจว่า
จอห์น Rasch

เหมือนกับคำตอบของ Genisio
nawfal

6

ฉันรู้ว่าคำตอบนี้สายเกินไป แต่นี่คือสิ่งที่ฉันทำ:

public class BaseAnimal : IEquatable<BaseAnimal>
{
    public string Name { private set; get; }
    public int Value { private set; get; }

    public BaseAnimal(int value, String name)
    {
        this.Name = name;
        this.Value = value;
    }

    public override String ToString()
    {
        return Name;
    }

    public bool Equals(BaseAnimal other)
    {
        return other.Name == this.Name && other.Value == this.Value;
    }
}

public class AnimalType : BaseAnimal
{
    public static readonly BaseAnimal Invertebrate = new BaseAnimal(1, "Invertebrate");

    public static readonly BaseAnimal Amphibians = new BaseAnimal(2, "Amphibians");

    // etc        
}

public class DogType : AnimalType
{
    public static readonly BaseAnimal Golden_Retriever = new BaseAnimal(3, "Golden_Retriever");

    public static readonly BaseAnimal Great_Dane = new BaseAnimal(4, "Great_Dane");

    // etc        
}

จากนั้นฉันสามารถทำสิ่งต่าง ๆ เช่น:

public void SomeMethod()
{
    var a = AnimalType.Amphibians;
    var b = AnimalType.Amphibians;

    if (a == b)
    {
        // should be equal
    }

    // call method as
    Foo(a);

    // using ifs
    if (a == AnimalType.Amphibians)
    {
    }
    else if (a == AnimalType.Invertebrate)
    {
    }
    else if (a == DogType.Golden_Retriever)
    {
    }
    // etc          
}

public void Foo(BaseAnimal typeOfAnimal)
{
}

2
หมายเลขมายากลสามารถถูกแทนที่ด้วยวัตถุตามstackoverflow.com/questions/757684/... แต่สำหรับกรณีนี้คุณสามารถได้รับประโยชน์สูงสุดจากทั้งสองโลกโดยใช้คุณสมบัติของระบบการตั้งชื่อทางชีวภาพที่มีอยู่เพื่อรับประกันเอกลักษณ์
ivan_pozdeev

4

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

// Base Class for balls 
public class BaseBall
{
    // keep synced with subclasses!
    public enum Sizes
    {
        Small,
        Medium,
        Large
    }
}

public class VolleyBall : BaseBall
{
    // keep synced with base class!
    public new enum Sizes
    {
        Small = BaseBall.Sizes.Small,
        Medium = BaseBall.Sizes.Medium,
        Large = BaseBall.Sizes.Large,
        SmallMedium,
        MediumLarge,
        Ginormous
    }
}

2
พิจารณาตั้งค่าช่วงที่แตกต่างกันสำหรับค่าใหม่ในคลาสที่ได้รับ (เช่นSmallMedium = 100,) ดังนั้นคุณสามารถใช้งานร่วมกับซอฟต์แวร์รุ่นเก่าได้เมื่อคุณเพิ่มค่าใหม่ในคลาสพื้นฐาน ตัวอย่างเช่นการเพิ่มHugeขนาดใน enum ฐานของคุณจะกำหนดให้4กับค่าของมัน แต่4ถูกใช้โดยSmallMediumในคลาสที่ได้รับ
Roberto

1
@ Roberto เพื่อแก้ไขปัญหานี้ฉันไม่เคยยืนยันคุณค่าของ enum แต่มีเพียงชื่อเท่านั้น และทำให้พวกเขาซิงค์เป็นข้อกำหนดที่นี่ ดังนั้นการเพิ่มHugeคลาสพื้นฐานจะต้องมีHugeคลาสย่อยก่อนSmallMedium
toddmo

2
BaseBallอาจไม่ใช่ชื่อที่ฉลาดที่สุดที่นี่ มันทำให้สับสน เนื่องจากเบสบอลเป็นเรื่องจริง หากคุณพลาดว่านี่เป็นเพียงคลาสเบสสำหรับบอลมันดูแปลกมากที่วอลเล่ย์บอลได้รับมรดกจากเบสบอลตัวเล็กกว่า :) ข้อเสนอแนะของฉันคือการใช้Ball
Nick N.

3

ทางเลือกการแก้ปัญหา

ใน บริษัท ของเราเราหลีกเลี่ยง "การกระโดดข้ามโครงการ" เพื่อไปยังโครงการระดับล่างทั่วไป ตัวอย่างเช่นเลเยอร์การนำเสนอ / API ของเราสามารถอ้างอิงชั้นโดเมนของเราเท่านั้นและชั้นโดเมนสามารถอ้างอิงชั้นข้อมูลได้เท่านั้น

อย่างไรก็ตามนี่เป็นปัญหาเมื่อมีจำนวน enums ที่ต้องมีการอ้างอิงโดยทั้งงานนำเสนอและชั้นโดเมน

นี่คือทางออกที่เราได้ดำเนินการ (จนถึงปัจจุบัน) มันเป็นทางออกที่ดีและทำงานได้ดีสำหรับเรา คำตอบอื่น ๆ ที่ถูกตีรอบนี้

หลักฐานพื้นฐานคือ enums ไม่สามารถสืบทอดได้ แต่คลาสสามารถทำได้ ดังนั้น...

// In the lower level project (or DLL)...
public abstract class BaseEnums
{
    public enum ImportanceType
    {
        None = 0,
        Success = 1,
        Warning = 2,
        Information = 3,
        Exclamation = 4
    }

    [Flags]
    public enum StatusType : Int32
    {
        None = 0,
        Pending = 1,
        Approved = 2,
        Canceled = 4,
        Accepted = (8 | Approved),
        Rejected = 16,
        Shipped = (32 | Accepted),
        Reconciled = (64 | Shipped)
    }

    public enum Conveyance
    {
        None = 0,
        Feet = 1,
        Automobile = 2,
        Bicycle = 3,
        Motorcycle = 4,
        TukTuk = 5,
        Horse = 6,
        Yak = 7,
        Segue = 8
    }

จากนั้นไปที่ "รับช่วง" จำนวนอื่นในโครงการระดับที่สูงขึ้น ...

// Class in another project
public sealed class SubEnums: BaseEnums
{
   private SubEnums()
   {}
}

นี่เป็นข้อได้เปรียบที่แท้จริงสามข้อ ...

  1. คำจำกัดความ enum จะเหมือนกันโดยอัตโนมัติในทั้งสองโครงการ - ตามคำจำกัดความ
  2. การเปลี่ยนแปลงใด ๆ ในนิยาม enum จะถูกสะท้อนในวินาทีโดยอัตโนมัติโดยไม่ต้องทำการแก้ไขใด ๆ กับคลาสที่สอง
  3. enums นั้นใช้รหัสเดียวกัน - ดังนั้นจึงสามารถเปรียบเทียบค่าได้อย่างง่ายดาย (กับคำเตือน)

ในการอ้างอิง enums ในโครงการแรกคุณสามารถใช้คำนำหน้าของคลาส: BaseEnums.StatusType.Pendingหรือเพิ่ม"การใช้ BaseEnums แบบคงที่"คำสั่งการใช้งานของคุณ

ในโครงการที่สองเมื่อจัดการกับคลาสที่สืบทอดมา แต่ฉันไม่สามารถใช้วิธี "ใช้ static ... " ในการทำงานดังนั้นการอ้างอิงทั้งหมดกับ "สืบทอดมา enums" จะนำหน้าด้วยคลาสเช่นSubEnums.StatusType.Pending . หากใครมาพร้อมกับวิธีการอนุญาตให้"ใช้คงที่"ในโครงการที่สองแจ้งให้เราทราบ

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

โปรดโหวตสิ่งนี้ถ้าคุณเห็นว่ามีประโยชน์


2

ฉันต้องการโอเวอร์โหลด Enums และสร้างคำตอบของ 'Seven' ในหน้านี้และคำตอบของ 'Merlyn Morgan-Graham' ในโพสต์ที่ซ้ำกันรวมถึงการปรับปรุงอีกสองสามอย่าง
ข้อได้เปรียบหลักของโซลูชันของฉันเหนือผู้อื่น:

  • การเพิ่มขึ้นโดยอัตโนมัติของค่า int พื้นฐาน
  • ตั้งชื่ออัตโนมัติ

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

ก่อนอื่นมีคลาสฐานCEnumที่กำหนดเอง enums ทั้งหมดควรสืบทอดจาก มันมีฟังก์ชั่นพื้นฐานคล้ายกับEnumประเภท. net :

public class CEnum
{
  protected static readonly int msc_iUpdateNames  = int.MinValue;
  protected static int          ms_iAutoValue     = -1;
  protected static List<int>    ms_listiValue     = new List<int>();

  public int Value
  {
    get;
    protected set;
  }

  public string Name
  {
    get;
    protected set;
  }

  protected CEnum ()
  {
    CommonConstructor (-1);
  }

  protected CEnum (int i_iValue)
  {
    CommonConstructor (i_iValue);
  }

  public static string[] GetNames (IList<CEnum> i_listoValue)
  {
    if (i_listoValue == null)
      return null;
    string[] asName = new string[i_listoValue.Count];
    for (int ixCnt = 0; ixCnt < asName.Length; ixCnt++)
      asName[ixCnt] = i_listoValue[ixCnt]?.Name;
    return asName;
  }

  public static CEnum[] GetValues ()
  {
    return new CEnum[0];
  }

  protected virtual void CommonConstructor (int i_iValue)
  {
    if (i_iValue == msc_iUpdateNames)
    {
      UpdateNames (this.GetType ());
      return;
    }
    else if (i_iValue > ms_iAutoValue)
      ms_iAutoValue = i_iValue;
    else
      i_iValue = ++ms_iAutoValue;

    if (ms_listiValue.Contains (i_iValue))
      throw new ArgumentException ("duplicate value " + i_iValue.ToString ());
    Value = i_iValue;
    ms_listiValue.Add (i_iValue);
  }

  private static void UpdateNames (Type i_oType)
  {
    if (i_oType == null)
      return;
    FieldInfo[] aoFieldInfo = i_oType.GetFields (BindingFlags.Public | BindingFlags.Static);

    foreach (FieldInfo oFieldInfo in aoFieldInfo)
    {
      CEnum oEnumResult = oFieldInfo.GetValue (null) as CEnum;
      if (oEnumResult == null)
        continue;
      oEnumResult.Name = oFieldInfo.Name;
    }
  }
}

ประการที่สองนี่คือ 2 คลาส Enum ที่ได้รับ คลาสที่ได้รับทั้งหมดต้องการวิธีการพื้นฐานบางอย่างเพื่อให้ทำงานได้ตามที่คาดไว้ มันเป็นรหัสสำเร็จรูปเดียวกันเสมอ ฉันยังไม่พบวิธีที่จะ outsource ให้กับคลาสฐาน รหัสของระดับแรกของการสืบทอดแตกต่างกันเล็กน้อยจากระดับถัดไปทั้งหมด

public class CEnumResult : CEnum
{
  private   static List<CEnumResult>  ms_listoValue = new List<CEnumResult>();

  public    static readonly CEnumResult Nothing         = new CEnumResult (  0);
  public    static readonly CEnumResult SUCCESS         = new CEnumResult (  1);
  public    static readonly CEnumResult UserAbort       = new CEnumResult ( 11);
  public    static readonly CEnumResult InProgress      = new CEnumResult (101);
  public    static readonly CEnumResult Pausing         = new CEnumResult (201);
  private   static readonly CEnumResult Dummy           = new CEnumResult (msc_iUpdateNames);

  protected CEnumResult () : base ()
  {
  }

  protected CEnumResult (int i_iValue) : base (i_iValue)
  {
  }

  protected override void CommonConstructor (int i_iValue)
  {
    base.CommonConstructor (i_iValue);

    if (i_iValue == msc_iUpdateNames)
      return;
    if (this.GetType () == System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType)
      ms_listoValue.Add (this);
  }

  public static new CEnumResult[] GetValues ()
  {
    List<CEnumResult> listoValue = new List<CEnumResult> ();
    listoValue.AddRange (ms_listoValue);
    return listoValue.ToArray ();
  }
}

public class CEnumResultClassCommon : CEnumResult
{
  private   static List<CEnumResultClassCommon> ms_listoValue = new List<CEnumResultClassCommon>();

  public    static readonly CEnumResult Error_InternalProgramming           = new CEnumResultClassCommon (1000);

  public    static readonly CEnumResult Error_Initialization                = new CEnumResultClassCommon ();
  public    static readonly CEnumResult Error_ObjectNotInitialized          = new CEnumResultClassCommon ();
  public    static readonly CEnumResult Error_DLLMissing                    = new CEnumResultClassCommon ();
  // ... many more
  private   static readonly CEnumResult Dummy                               = new CEnumResultClassCommon (msc_iUpdateNames);

  protected CEnumResultClassCommon () : base ()
  {
  }

  protected CEnumResultClassCommon (int i_iValue) : base (i_iValue)
  {
  }

  protected override void CommonConstructor (int i_iValue)
  {
    base.CommonConstructor (i_iValue);

    if (i_iValue == msc_iUpdateNames)
      return;
    if (this.GetType () == System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType)
      ms_listoValue.Add (this);
  }

  public static new CEnumResult[] GetValues ()
  {
    List<CEnumResult> listoValue = new List<CEnumResult> (CEnumResult.GetValues ());
    listoValue.AddRange (ms_listoValue);
    return listoValue.ToArray ();
  }
}

ชั้นเรียนได้รับการทดสอบเรียบร้อยแล้วด้วยรหัส follwing:

private static void Main (string[] args)
{
  CEnumResult oEnumResult = CEnumResultClassCommon.Error_Initialization;
  string sName = oEnumResult.Name;   // sName = "Error_Initialization"

  CEnum[] aoEnumResult = CEnumResultClassCommon.GetValues ();   // aoEnumResult = {testCEnumResult.Program.CEnumResult[9]}
  string[] asEnumNames = CEnum.GetNames (aoEnumResult);
  int ixValue = Array.IndexOf (aoEnumResult, oEnumResult);    // ixValue = 6
}

1

Enums ไม่ใช่คลาสจริงแม้ว่าพวกเขาจะดูเหมือน ภายในจะได้รับการปฏิบัติเช่นเดียวกับประเภทพื้นฐานของพวกเขา (โดยค่าเริ่มต้น Int32) ดังนั้นคุณสามารถทำได้โดยการ "คัดลอก" ค่าเดียวจาก enum หนึ่งไปยังอีกค่าและส่งไปยังหมายเลขจำนวนเต็มเพื่อเปรียบเทียบเพื่อความเท่าเทียมกัน


1

Enums ไม่สามารถถูกแยกออกจาก enums อื่น ๆ ได้ แต่จาก int, uint, short, ushort, long, ulong, byte และ sbyte

เช่นเดียวกับ Pascal กล่าวว่าคุณสามารถใช้ค่าหรือค่าคงที่อื่น ๆ ของ enum เพื่อเริ่มต้นค่า enum แต่นั่นเป็นเรื่องเกี่ยวกับมัน


8
มันเป็นบิตของการเรียกชื่อผิดเนื่องจากไวยากรณ์ c # แต่ enum ไม่สามารถสืบทอดจาก int, uint, etc ... ภายใต้ประทุนพวกเขายังคงสืบทอดมาจาก System.Enum มันเป็นเพียงว่าสมาชิกที่เป็นตัวแทนของ enum ถูกพิมพ์ไป int ที่ UINT ฯลฯ ...
JaredPar

@JaredPar เมื่อ enum ถูกสกัดออกมาจาก uint มันหมายความว่ามันเป็นค่าที่เป็น uint ทั้งหมดเป็นต้นโดยค่าเริ่มต้นแล้ว enum จะรับค่า int (ดูที่ข้อกำหนด C #, enum SomeEnum: uint {... } ใช้งานได้จริง)
Jeroen Landheer

4
ไม่จริง มันสืบทอด System.enum ตามที่โพสต์ก่อนหน้านี้และบ่อยขึ้นที่นี่สิ่งที่คุณคิดว่าเป็นมรดกเป็นเพียงความสับสนวุ่นวายใน csharp
TomTom

1

แนวทางแก้ไขที่เป็นไปได้อื่น:

public enum @base
{
    x,
    y,
    z
}

public enum consume
{
    x = @base.x,
    y = @base.y,
    z = @base.z,

    a,b,c
}

// TODO: Add a unit-test to check that if @base and consume are aligned

HTH


1

ไม่สามารถทำได้ (ตามที่ @JaredPar พูดถึงแล้ว) การพยายามวางตรรกะเพื่อแก้ไขสิ่งนี้เป็นวิธีปฏิบัติที่ไม่ดี ในกรณีที่คุณมีสิ่งbase classนั้นenumคุณควรทำรายการที่เป็นไปได้ทั้งหมดenum-valuesและการใช้งานคลาสควรทำงานกับค่าที่รู้

เช่นสมมติว่าคุณมีคลาสพื้นฐานBaseCatalogและมีenum ProductFormats( Digital, Physical) จากนั้นคุณสามารถมีMusicCatalogหรือBookCatalogที่อาจมีทั้งDigitalและPhysicalผลิตภัณฑ์ แต่ถ้าชั้นเรียนClothingCatalogก็ควรมีเพียงPhysicalผลิตภัณฑ์


1

ฉันรู้ว่าฉันมาช้าไปงานปาร์ตี้นี้ แต่นี่คือสองเซ็นต์ของฉัน

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

แนะนำ: ObjectEnum

คุณสามารถตรวจสอบรหัสและเอกสารได้ที่นี่: https://github.com/dimi3tron/ObjectEnum https://github.com/dimi3tron/ObjectEnum

และแพคเกจที่นี่: https://www.nuget.org/packages/ObjectEnum

หรือเพียงติดตั้ง: Install-Package ObjectEnum

ในระยะสั้น ObjectEnum<TEnum>ทำหน้าที่เป็นเสื้อคลุมสำหรับ enum ใด ๆ โดยการแทนที่ GetDefinedValues ​​() ในคลาสย่อยหนึ่งสามารถระบุค่า enum ที่ถูกต้องสำหรับคลาสเฉพาะนี้

มีการเพิ่มจำนวนผู้ให้บริการเกินจำนวนเพื่อเพิ่ม ObjectEnum<TEnum>อินสแตนซ์ทำงานเสมือนว่าเป็นอินสแตนซ์ของ enum พื้นฐานโดยคำนึงถึงข้อ จำกัด ค่าที่กำหนดไว้ ซึ่งหมายความว่าคุณสามารถเปรียบเทียบอินสแตนซ์กับค่า int หรือ enum ได้อย่างง่ายดายและใช้มันในกรณีสวิตช์หรือเงื่อนไขอื่น ๆ

ฉันต้องการอ้างถึง repo GitHub กล่าวถึงข้างต้นสำหรับตัวอย่างและข้อมูลเพิ่มเติม

ฉันหวังว่าคุณจะพบว่ามีประโยชน์นี้ รู้สึกอิสระที่จะแสดงความคิดเห็นหรือเปิดปัญหาเกี่ยวกับ GitHub สำหรับความคิดหรือความคิดเห็นเพิ่มเติม

นี่คือตัวอย่างสั้น ๆ ของสิ่งที่คุณสามารถทำได้ObjectEnum<TEnum>:

var sunday = new WorkDay(DayOfWeek.Sunday); //throws exception
var monday = new WorkDay(DayOfWeek.Monday); //works fine
var label = $"{monday} is day {(int)monday}." //produces: "Monday is day 1."
var mondayIsAlwaysMonday = monday == DayOfWeek.Monday; //true, sorry...

var friday = new WorkDay(DayOfWeek.Friday);

switch((DayOfWeek)friday){
    case DayOfWeek.Monday:
        //do something monday related
        break;
        /*...*/
    case DayOfWeek.Friday:
        //do something friday related
        break;
}

-8

คุณสามารถทำการสืบทอดได้ใน enum อย่างไรก็ตาม จำกัด เฉพาะประเภทต่อไปนี้เท่านั้น int, uint, byte, sbyte, สั้น, ushort, ยาว, ulong

เช่น

public enum Car:int{
Toyota,
Benz,
}

2
ผมคิดว่า OP ก็ขอให้ได้รับมรดกหนึ่ง enum จากที่อื่นไม่เพียง แต่จากฐานประเภทที่เป็นตัวเลข (ซึ่ง enums ทุกคนทำใน C # ทั้งโดยปริยายหรืออย่างชัดเจน.)
reirab
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.