听起来您真正想要的是 sum 类型。尽管 C# 没有内置这些功能,但您可以使用函数式编程中的一个技巧,称为 Church 编码来实现这一点。它是完全类型安全的,不涉及强制转换,但是在 C# 中使用它有点奇怪,主要是由于类型推断的限制。
主要技巧是,我们没有使用属性和检查来检索两个备选方案之一,而是有一个高阶函数Map
,它接受两个函数作为参数,并根据存在的备选方案调用适当的函数。以下是您将如何使用它:
var stack = new Stack<IEither<Operator, Parenthesis>>();
stack.Push(new Left<Operator, Parenthesis>(new Operator()));
stack.Push(new Right<Operator, Parenthesis>(new Parenthesis()));
while (stack.Count > 0)
{
stack.Pop().Map(op => Console.WriteLine("Found an operator: " + op),
par => Console.WriteLine("Found a parenthesis: " + par));
}
这是IEither
,Left
和的实现Right
。它们是完全通用的,可以在任何需要 sum 类型的地方使用。
public interface IEither<TLeft, TRight>
{
TResult Map<TResult>(Func<TLeft, TResult> onLeft, Func<TRight, TResult> onRight);
void Map(Action<TLeft> onLeft, Action<TRight> onRight);
}
public sealed class Left<TLeft, TRight> : IEither<TLeft, TRight>
{
private readonly TLeft value;
public Left(TLeft value)
{
this.value = value;
}
public TResult Map<TResult>(Func<TLeft, TResult> onLeft, Func<TRight, TResult> onRight)
{
return onLeft(value);
}
public void Map(Action<TLeft> onLeft, Action<TRight> onRight)
{
onLeft(value);
}
}
public sealed class Right<TLeft, TRight> : IEither<TLeft, TRight>
{
private readonly TRight value;
public Right(TRight value)
{
this.value = value;
}
public TResult Map<TResult>(Func<TLeft, TResult> onLeft, Func<TRight, TResult> onRight)
{
return onRight(value);
}
public void Map(Action<TLeft> onLeft, Action<TRight> onRight)
{
onRight(value);
}
}
参考: