C# 类型安全在集合中存储两种类型对象的方法
我已经实现了一个增强的调车场算法,用于解析算术表达式。该算法的一个方面是,它维护一个C# 类型安全在集合中存储两种类型对象的方法,c#,collections,types,heterogeneous,C#,Collections,Types,Heterogeneous,我已经实现了一个增强的调车场算法,用于解析算术表达式。该算法的一个方面是,它维护一个队列,以及一个堆栈 在我的实现中,队列包含表达式和运算符。 堆栈包含运算符和括号 表达式、括号和运算符之间没有任何共同点可以保证它们中的任何两个具有共享接口 方法: 我当前的实现包括Expression和Operator实现inotpranthesis操作员和偏执执行INotExpression。然后我声明队列,和堆栈 我不喜欢这种实现——这些接口似乎是为了更干净的算法代码而进行的黑客攻击。我还认为接口应该描述
队列
,以及一个堆栈
在我的实现中,队列
包含表达式
和运算符
。
堆栈
包含运算符
和括号
表达式
、括号
和运算符
之间没有任何共同点可以保证它们中的任何两个具有共享接口
方法:
- 我当前的实现包括
和Expression
实现Operator
<代码>操作员和inotpranthesis
执行偏执
。然后我声明INotExpression
,和队列
我不喜欢这种实现——这些接口似乎是为了更干净的算法代码而进行的黑客攻击。我还认为接口应该描述对象是什么,而不是它不是什么堆栈
- 另一方面,我也不想使用
的集合,因为很难确定这些代码的正确性 - 到目前为止,我唯一想到的方法是实现我自己的
和NonParanthesisQueue
容器。这样做的优点是对从这些容器中取出的对象进行更一致的类型检查,缺点是代码太多NonExpressionStack
是否有合理的替代方法?也许您可以为每种方法定义一个小持有者类型,一个使用表达式属性和运算符属性,另一个使用运算符属性和括号属性。访问器和构造函数可以断言或以其他方式确保只填充一个。队列和堆栈将分别包含适当的持有者类型 有点笨拙,但打字安全,可操作
希望有人会有一个更聪明的想法。听起来你真正想要的是求和类型。尽管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));
}
参考资料:
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);
}
}