[注:此问题的原标题为“ C#中的C(ish)样式联合 ”,但正如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可以是另一个小组的类型。我不喜欢处理这些值作为对象(我想要的温暖的贴身感觉的编码,并带有一些类型安全性)。
ValueA
string
int
Foo
ValueB
因此,我考虑编写一个琐碎的小包装器类,以表达ValueA在逻辑上是对特定类型的引用这一事实。我Union之所以打电话给全班,是因为我想达到的目标使我想起了C语言中的联合概念。
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是否为Value是无效的,char因为其定义清楚地表明不是- 这是编程错误,我希望编译器对此有所了解。[另外,如果我能正确地做到这一点,(希望)我也将获得智能感知-这将是一个福音。
char
为了实现这一点,我想告诉编译器,该类型T可以是A,B或C中的一种
T
public bool Is<T>() where T : A or T : B // Yes I know this is not legal! or T : C { return typeof(T) == type; }
有谁知道我想要实现的目标是否可行?还是我只是因为一开始就写这个课程而变得愚蠢?
提前致谢。
我真的不喜欢上面提供的类型检查和类型转换解决方案,因此这里是100%类型安全的联合,如果您尝试使用错误的数据类型,它将引发编译错误:
using System; namespace Juliet { class Program { static void Main(string[] args) { Union3<int, char, string>[] unions = new Union3<int,char,string>[] { new Union3<int, char, string>.Case1(5), new Union3<int, char, string>.Case2('x'), new Union3<int, char, string>.Case3("Juliet") }; foreach (Union3<int, char, string> union in unions) { string value = union.Match( num => num.ToString(), character => new string(new char[] { character }), word => word); Console.WriteLine("Matched union with value '{0}'", value); } Console.ReadLine(); } } public abstract class Union3<A, B, C> { public abstract T Match<T>(Func<A, T> f, Func<B, T> g, Func<C, T> h); // private ctor ensures no external classes can inherit private Union3() { } public sealed class Case1 : Union3<A, B, C> { public readonly A Item; public Case1(A item) : base() { this.Item = item; } public override T Match<T>(Func<A, T> f, Func<B, T> g, Func<C, T> h) { return f(Item); } } public sealed class Case2 : Union3<A, B, C> { public readonly B Item; public Case2(B item) { this.Item = item; } public override T Match<T>(Func<A, T> f, Func<B, T> g, Func<C, T> h) { return g(Item); } } public sealed class Case3 : Union3<A, B, C> { public readonly C Item; public Case3(C item) { this.Item = item; } public override T Match<T>(Func<A, T> f, Func<B, T> g, Func<C, T> h) { return h(Item); } } } }