[หมายเหตุ: คำถามนี้มีชื่อเดิมว่า " C (ish) style union ใน C # " แต่ตามที่ความคิดเห็นของ Jeff แจ้งให้ฉันทราบว่าโครงสร้างนี้เรียกว่า 'การรวมกลุ่มที่เลือกปฏิบัติ']
ขอโทษคำฟุ่มเฟือยของคำถามนี้
มีคำถามที่ทำให้เกิดเสียงคล้าย ๆ กันอยู่สองข้อใน SO แต่ดูเหมือนว่าพวกเขาจะมุ่งเน้นไปที่ประโยชน์ในการประหยัดหน่วยความจำของสหภาพหรือใช้เพื่อการทำงานร่วมกัน นี่คือตัวอย่างของคำถามดังกล่าว
ความปรารถนาของฉันที่จะมีสิ่งที่เป็นสหภาพแตกต่างกันบ้าง
ฉันกำลังเขียนโค้ดบางอย่างในขณะนี้ซึ่งสร้างวัตถุที่มีลักษณะเช่นนี้
public class ValueWrapper
{
public DateTime ValueCreationDate;
// ... other meta data about the value
public object ValueA;
public object ValueB;
}
สิ่งที่ค่อนข้างซับซ้อนฉันคิดว่าคุณจะเห็นด้วย เป็นสิ่งที่ValueA
สามารถเป็นของบางประเภทไม่กี่ (สมมติว่าstring
, int
และFoo
(ซึ่งเป็นชั้นเรียน) และValueB
สามารถเป็นอีกชุดเล็ก ๆ ของประเภท. ฉันไม่ชอบการรักษาค่าเหล่านี้เป็นวัตถุ (ฉันต้องการที่อบอุ่นอย่างอบอุ่นความรู้สึกของ การเข้ารหัสด้วยความปลอดภัยเล็กน้อย)
ดังนั้นฉันจึงคิดเกี่ยวกับการเขียนคลาส wrapper เล็ก ๆ น้อย ๆ เพื่อแสดงความจริงที่ว่า ValueA มีเหตุผลเป็นการอ้างอิงถึงประเภทใดประเภทหนึ่ง ฉันเรียกชั้นเรียนUnion
เพราะสิ่งที่ฉันพยายามจะบรรลุทำให้ฉันนึกถึงแนวคิดการรวมกลุ่มในค.
public class Union<A, B, C>
{
private readonly Type type;
public readonly A a;
public readonly B b;
public readonly C c;
public A A{get {return a;}}
public B B{get {return b;}}
public C C{get {return c;}}
public Union(A a)
{
type = typeof(A);
this.a = a;
}
public Union(B b)
{
type = typeof(B);
this.b = b;
}
public Union(C c)
{
type = typeof(C);
this.c = c;
}
/// <summary>
/// Returns true if the union contains a value of type T
/// </summary>
/// <remarks>The type of T must exactly match the type</remarks>
public bool Is<T>()
{
return typeof(T) == type;
}
/// <summary>
/// Returns the union value cast to the given type.
/// </summary>
/// <remarks>If the type of T does not exactly match either X or Y, then the value <c>default(T)</c> is returned.</remarks>
public T As<T>()
{
if(Is<A>())
{
return (T)(object)a; // Is this boxing and unboxing unavoidable if I want the union to hold value types and reference types?
//return (T)x; // This will not compile: Error = "Cannot cast expression of type 'X' to 'T'."
}
if(Is<B>())
{
return (T)(object)b;
}
if(Is<C>())
{
return (T)(object)c;
}
return default(T);
}
}
การใช้คลาส ValueWrapper ตอนนี้จะมีลักษณะเช่นนี้
public class ValueWrapper2
{
public DateTime ValueCreationDate;
public Union<int, string, Foo> ValueA;
public Union<double, Bar, Foo> ValueB;
}
ซึ่งเป็นสิ่งที่เหมือนกับสิ่งที่ฉันต้องการบรรลุ แต่ฉันขาดองค์ประกอบที่สำคัญพอสมควรนั่นคือคอมไพเลอร์บังคับใช้การตรวจสอบประเภทเมื่อเรียกใช้ฟังก์ชัน Is และ As ตามที่โค้ดต่อไปนี้แสดงให้เห็น
public void DoSomething()
{
if(ValueA.Is<string>())
{
var s = ValueA.As<string>();
// .... do somethng
}
if(ValueA.Is<char>()) // I would really like this to be a compile error
{
char c = ValueA.As<char>();
}
}
IMO ไม่ถูกต้องที่จะถาม ValueA หากเป็นchar
เพราะคำจำกัดความของมันบอกอย่างชัดเจนว่าไม่ใช่ - นี่เป็นข้อผิดพลาดในการเขียนโปรแกรมและฉันต้องการให้คอมไพเลอร์รับสิ่งนี้ [นอกจากนี้ถ้าฉันสามารถแก้ไขสิ่งนี้ได้ (หวังว่า) ฉันจะได้รับ Intellisense ด้วย
เพื่อให้บรรลุสิ่งนี้ฉันต้องการบอกคอมไพเลอร์ว่าประเภทT
สามารถเป็น A, B หรือ C ได้
public bool Is<T>() where T : A
or T : B // Yes I know this is not legal!
or T : C
{
return typeof(T) == type;
}
ใครมีความคิดบ้างไหมว่าสิ่งที่ฉันต้องการบรรลุเป็นไปได้ หรือฉันโง่ธรรมดาที่เขียนคลาสนี้ตั้งแต่แรก?
ขอบคุณล่วงหน้า.
StructLayout(LayoutKind.Explicit)
FieldOffset
สิ่งนี้ไม่สามารถทำได้กับประเภทอ้างอิงแน่นอน สิ่งที่คุณกำลังทำไม่เหมือน C Union เลย