[英]Generics in enum
我有一个数据库,可以容纳字符串和整数作为值。 当我浏览数据库时,我想添加每个数据槽以及数据库中的内容。
我决定制作一个可以接受int或string的收集器类,我想创建一个如下的枚举:
哈希码:
enum Either<A,B>
{
Left( v:A );
Right( v:B );
}
例如,A可以是int,B可以是字符串。
这是可以在C#中做到的还是模仿此功能的最佳方法是什么:保持强类型(无动态变量)进行编译器类型检查。
编辑 :
我无法使用Tuple,因为它在Unity3D中不支持
枚举基本上只是常量的集合。 每个常量都有一个标识符,应该是一个友好的名称,您可以在代码中使用该标识符,并且在幕后使用数字值。 因此,您的问题的答案为“否”。
也就是说,如果您有枚举值,则可以调用其ToString方法将其转换为包含标识符名称的String。 我认为您也可以将其强制转换为int或在类型声明中指定的任何数字类型。 Enum类还具有sone静态方法,该方法将进行其他转换。
不能以您描述的方式拥有通用枚举。 但是,从创建自己的Tuple实现中止什么呢?
public class Tuple<TLeft, TRight> {
public TLeft Left { get; set; }
public TRight Right { get; set; }
}
如果您需要unity3d中经常需要的序列化支持,请使用SerializableAttibute
装饰您的类。 您可以进行任何本机.NET Tuple内部调查和某些有用部分的复制。
请注意,此人似乎不是在请求Tuple(一种包含ac样式结构但没有名称的几种类型的Tuple),而是Tagged Union / Sum type 。
元组具有每种类型中的一种,带标记的联合仅包含2种或更多类型中的一个值。
他给出的示例是一个haxe枚举,它不同于ac#枚举(基本上是数字的名称)。
注意:Haxe Enums的行为与带标记的联合非常相似,从而可以指定和捕获给定方法结果的每个“个案”。 由于必须在switch语句中指定所有Enum状态,因此这对于完全定义给定方法的行为以及确保处理这些行为很有价值。
如果要确保始终处理所有情况,请不要在下面的类中实现单个Action Run方法。
您也可能要避免使用名称Either,因为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));
如果您想以更强制性/稍微快一些/不太安全的方式使用它,请将标记和Values公开(或将标记公开,并使用Get(A / B / C / D / E)OrThrow方法启用更好的异常您以错误的方式使用),并使用if / else或将其打开以使用AValue / BValue(如果使用的是3/4/5元组,则使用CValue / DValue / EValue)。 请注意,这不可能强迫您处理所有状态,并且更有可能使您弄乱并使用错误的类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.