我有一个数据库,可以将字符串和整数作为值。 当我通过数据库时,我想用数据库中的内容添加每个数据槽。
我决定创建一个可以接受int或string的收集器类,我想创建一个这样的枚举:
Haxe代码:
enum Either<A,B>
{
Left( v:A );
Right( v:B );
}
例如A可以是int和B - string。
这是否可以在C#中进行,或者是模仿此功能的最佳方法:保持强类型(无动态变量)以进行编译器类型检查。
修改:
我不能使用Tuple,因为它在Unity3D中没有支持
答案 0 :(得分:0)
枚举基本上只是常量的集合。每个常量都有一个标识符,它应该是一个友好的名称,您可以在代码中使用,并在引擎盖下使用数值。因此,你的问题的答案是“不”。
也就是说,如果你有一个枚举值,那么你可以调用它的ToString方法将它转换为包含标识符名称的String。我认为您也可以将它转换为int或您在类型声明中指定的任何数字类型。 Enum类还有一些静态方法,它们将转换另一种方式以及更多方法。
答案 1 :(得分:0)
不可能以您描述的方式使用通用枚举。但是,从你自己的Tuple实现的创建中停止了什么呢?
public class Tuple<TLeft, TRight> {
public TLeft Left { get; set; }
public TRight Right { get; set; }
}
如果您需要在unity3d中经常需要的序列化支持,请使用SerializableAttibute
装饰您的班级。您可以随意调查本机.NET Tuple的内部结构并复制一些有用的部分。
答案 2 :(得分:0)
注意,这个人似乎没有要求一个元组(一种类型包含几种类型,如c风格的结构但没有名字),但是Tagged Union/Sum type。
元组具有每种类型中的一种,标记的联合仅包含两种或更多种类型的值。
他给出的例子是一个haxe枚举,它不像c#enum(基本上是一个数字的名字)。
注意: Haxe Enums的行为与标记的联合非常相似,可以指定并捕获给定方法结果的每个“案例”。由于必须在switch语句中指定所有Enum状态,因此这使得它们在完全定义给定方法的行为以及确保处理这些行为方面具有价值。
如果您想确保始终处理每个案例,请不要在下面的类中实现单个Action Run方法。
此外,您可能希望避免使用名称“Either”(使用左/右)经常使用具体作为错误或结果的并集(左侧是错误或右侧是结果
这是一个2成员联盟的一个实现。
您可能还需要具有相同名称的3,4,5成员联盟的副本
public sealed class Union<A,B,C> : IEquatable<Union<A,B>>
public sealed class Union<A,B,C,D> : IEquatable<Union<A,B,C,D>>
public sealed class Union<A,B,C,D,E> : IEquatable<Union<A,B,C,D,E>>
以下一个模式
[Serializable()]
public sealed class Union<A,B> : IEquatable<Union<A,B>>
{
private enum TypeTag{ A, B};
private Union ()
{
}
private A AValue;
private B BValue;
private TypeTag tag;
public static Union<A,B> CreateA (A a)
{
var u = new Union<A,B> ();
u.AValue = a;
u.tag = TypeTag.A;
return u;
}
public static Union<A,B> CreateB (B b)
{
var u = new Union<A,B> ();
u.BValue = b;
u.tag = TypeTag.B;
return u;
}
public U SelectOn<U> (Func<A, U> withA, Func<B, U> withB)
{
if (withA == null)
throw new ArgumentNullException ("withA");
if (withB == null)
throw new ArgumentNullException ("withB");
if (tag == TypeTag.A)
{
return withA (AValue);
}
else if (tag == TypeTag.B)
{
return withB (BValue);
}
throw new InvalidOperationException ("Unreachable code.");
}
public void Run (Action<A> actionIfA, Action<B> actionIfB)
{
if (actionIfA == null)
throw new ArgumentNullException ("actionIfA");
if (actionIfB == null)
throw new ArgumentNullException ("actionIfB");
if (tag == TypeTag.A)
{
actionIfA (AValue);
}
else if (tag == TypeTag.B)
{
actionIfB (BValue);
}
}
public void Run (Action<A> actionIfA)
{
if (actionIfA == null)
throw new ArgumentNullException ("actionIfA");
if (tag == TypeTag.A)
{
actionIfA (AValue);
}
}
public void Run (Action<B> actionIfB)
{
if (actionIfB == null)
throw new ArgumentNullException ("actionIfB");
if (tag == TypeTag.B) {
actionIfB (BValue);
}
}
public override string ToString ()
{
if (tag == TypeTag.A)
{
return "Type A" + typeof(A).ToString() + ": " + AValue.ToString();
}
else if (tag == TypeTag.B) {
return "Type B" + typeof(B).ToString() + ": " + BValue.ToString();
}
throw new InvalidOperationException ("Unreachable code.");
}
public override int GetHashCode()
{
unchecked
{
int result = tag.GetHashCode();
if (tag == TypeTag.A) {
result = (result * 397) ^ (AValue != null ? AValue.GetHashCode() : 0);
} else if (tag == TypeTag.B) {
result = (result * 397) ^ (BValue != null ? BValue.GetHashCode() : 0);
}
return result;
}
}
public override bool Equals (object other)
{
if (other is Union<A,B>)
{
return this.Equals((Union<A,B>)other);
}
return false;
}
public bool Equals (Union<A,B> other)
{
if (this.tag != other.tag)
{
return false;
}
if (tag == TypeTag.A)
{
return this.AValue.Equals(other.AValue);
}
else if (tag == TypeTag.B)
{
return this.AValue.Equals(other.AValue);
}
return false;
}
}
示例用法:
var i = Union<int,string>.CreateA(5);
var s = Union<int,string>.CreateB("Fre");
s.Run(actionIfA: n => { Console.WriteLine("1.number*3 is " + n*3); },
actionIfB: str => { Console.WriteLine("1.uppercase string is " + str.ToUpper()); });
var r = i.SelectOn(withA: n => "2.number*3 is" + n*3 , withB: str=> "2.uppercase string is" + str.ToUpper());
Console.WriteLine(r);
s.Run(actionIfA: n => { Console.WriteLine("3. number*3 is " + n*3); });
s.Run(actionIfB: str => { Console.WriteLine("4. uppercase string is " + str.ToUpper()); });
i.Run(actionIfA: n => { Console.WriteLine("5. number*3 is " + n*3); });
Console.WriteLine("does i equals s:" + i.Equals(s));
如果你想以更迫切/更快/更不安全的方式使用它 使标记和值公开(或使标记公开并使用Get(A / B / C / D / E)OrThrow方法,以便在使用错误的方式时启用更好的异常)并使用if / else或将其切换为使用AValue / BValue(如果你使用3/4/5元组,则为/ CValue / DValue / EValue)。请注意,这使得无法强制您处理所有状态并使您更有可能陷入困境并使用错误的类型。