C#中的计算数组值 - 反应式编程

时间:2011-10-24 09:08:03

标签: c# arrays multidimensional-array reactive-programming computed-values

示例1

正如我们所知,数据库中的计算列的概念是根据相邻列的值计算列。

问题是计算列不能与当前的其他行相关联。

示例2

然后我们有电子表格(即Excel),其中一个单元格可以有一个公式。这类似于计算列,但更强大。公式可以与电子表格中的任何单元格(或其中的一组)相关,而不仅仅与RDB中的当前行/列相关。

问题

计算(或自动更新的值)的概念很棒但是如何在C#中的一组值/对象中做类似的事情?

我想创建一个值列表(或数组),其中每个值都与同一列表中的其他一些(或一组)值相关?它就像电子表格单元格值...更改值和相关值也会发生变化(以及整个相关的子树值)。

在C#中有这样的概念吗?

我如何以最简单的聪明方式执行此操作?我知道我可以拥有LinkedList个对象,其中对象的属性值与同一列表中的其他对象相关,并在访问值时进行评估(按需评估)。这可能意味着在此过程中会对几个(所有祖先节点)进行评估。是否有更好的方法可以更像电子表格单元格,首先进行评估然后单独访问(传播评估)?

这当然也适用于多维数组。

4 个答案:

答案 0 :(得分:3)

这个概念叫做Reactive Programming。 .NET有一个名为Reactive Extensions的东西,可以让你实现你所描述的。具体而言,您需要使用名为行为

的内容

答案 1 :(得分:2)

您可以使用包含计算的索引器属性创建一个对象:

class Foo {
    public int this[int index] {
        get {
            return index*2; //Your computation here
        }
    }
}

class Foo {
    public int this[int row,int col] {
        get {
            return row*col; //Your computation here
        }
    }
}

或者,如果您想使用链表或某些链接列表,您可以在列表结构中存储“Cell”对象,并在这些“Cell”对象上具有属性或方法来执行计算:

class Cell {
    private int _row;
    private int _col;

    public Cell(int row,int col) {
        _row = row;
        _col = col;
    }

    public int Value {
        get {
            return _row * _col;
        }
    }
}

答案 2 :(得分:1)

您可以创建自己的数组类,它接受函数而不是值:

class ComputedArray<T>
{
    private Func<T>[] _array;

    public T this[int index] { get { return _array[index]( ); } }

    public void Set(int index, Func<T> func)
    {
        _array[index] = func;
    }

    public ComputedArray( int size )
    {
        _array = new Func<T>[size];
    }
}

现在您可以使用lambda表达式存储值:

ComputedArray<int> ar = new ComputedArray<int>( 2 );
ar.Set( 0, ( ) => 2 );
ar.Set( 1, ( ) => ar[0]*2 );
Console.WriteLine( ar[0] );
Console.WriteLine( ar[1] );

答案 3 :(得分:1)

这对于OO语言来说是完美的参考方式。在抽象层面,我会这样处理:

创建一个抽象类Expression,它将成为程序中所有值的基本类型。类似的东西:

public abstract class Expression
{
    List<Expression> linkedExpressions;
    protected Expression lhs; // left hand side, right hand side
    protected Expression rhs;

    protected Expression(Expression x, Expression y)
    {
        List<Expression> linkedExpressions = new List<Expression>();
        lhs = x;
        rhs = y;
        // let the expressions know that they have a expression dependant on them
        lhs.NotifyExpressionLinked(this); 
        rhs.NotifyExpressionLinked(this);
    }

    private void NotifyExpressionLinked(Expression e)
    {
        if (e != null)
        {
            linkedExpressions.Add(e);
        }
    }

    private void NotifyExpressionUnlinked(Expression e)
    {
        if (linkedExpressions.Contains(e)
        {
            linkedExpressions.Remove(e);
        }
    }

    // this method will notify all subscribed expressions that
    // one of the values they are dependant on has changed
    private void NotifyExpressionChanged()
    {
        if (linkedExpressions.Count != 0) // if we're not a leaf node
        {
            foreach (Expression e in linkedExpressions)
            {
                e.NotifyExpressionChanged();
            }
        }
        else Evaluate() 
        // once we're at a point where there are no dependant expressions 
        // to notify we can start evaluating
    }

    // if we only want to update the lhs, y will be null, and vice versa
    public sealed void UpdateValues(Expression x, Expression y)
    {
        if (x != null)
        {
            lhs.NotifyExpressionUnlinked(this);
            x.NotifyExpressionLinked(this);
            lhs = x;
        }

        if (y != null)
        {
            rhs.NotifyExpressionUnlinked(this);
            y.NotifyExpressionLinked(this);
            rhs = y;
        }

        NotifyExpressionChanged();
    }

    public virtual float Evaluate()
    {
        throw new NotImplementedException(); // we expect child classes to implement this
    }
}

为我们需要的每种表达式创建一个类。在最底部,您将有LiteralExpression,这只是一个数字:

public class LiteralExpression : Expression
{
    private float value;

    public LiteralExpression(float x)
        : base(null, null) { } // should not have any linked expressions

    public override float Evaluate()
    {
        return value;
    }
}

关于这个类有一点需要注意 - 由于它的工作方式,不应该在它上面使用UpdateValue()。相反,只需创建一个新的LiteralExpression来替换它。

然后,您需要为所需的所有表达式构建子类(例如,这里是另外的):

public class AdditionExpression : Expression
{
    public AdditionExpression(Expression x, Expression y)
        : base(x, y) { };

    public override float Evaluate()
    {
        return lhs.Evaluate() + rhs.Evaluate();
    }
}

这不是我们必须为每个表达式编写的所有代码 - 所有繁重的工作都由抽象类处理。这个程序有一些设计缺陷 - 它不会检测循环引用,也不会阻止你将null值传递给表达式(在需要LiteralExpression的情况下),但这些问题不应该不太难解决。

然后,所有需要做的就是实现从表达式继承的所有子类。

可能有更好的方法来实现这一点,但从OO的角度来看,这是一个很好的例子,它可以创建一个通用的抽象类来实现常见的行为,并为该类的每个“风味”提供许多小的,特定的实现。