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));
}

参考资料:


谢谢——这不是我考虑过的——虽然有点冗长,但绝对有道理。谢谢!这正是我想要的。@Vladislav这与Rationalull的答案类似(基本上是使用元组类型来保存异构数据),但我想你可以将两者的想法结合起来,使其更简短(我说这是因为你喜欢它不那么冗长)。
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);
    }
}