正如我们所知,数据库中的计算列的概念是根据相邻列的值计算列。
问题是计算列不能与当前的其他行相关联。
然后我们有电子表格(即Excel),其中一个单元格可以有一个公式。这类似于计算列,但更强大。公式可以与电子表格中的任何单元格(或其中的一组)相关,而不仅仅与RDB中的当前行/列相关。
计算(或自动更新的值)的概念很棒但是如何在C#中的一组值/对象中做类似的事情?
我想创建一个值列表(或数组),其中每个值都与同一列表中的其他一些(或一组)值相关?它就像电子表格单元格值...更改值和相关值也会发生变化(以及整个相关的子树值)。
在C#中有这样的概念吗?
我如何以最简单的聪明方式执行此操作?我知道我可以拥有LinkedList
个对象,其中对象的属性值与同一列表中的其他对象相关,并在访问值时进行评估(按需评估)。这可能意味着在此过程中会对几个(所有祖先节点)进行评估。是否有更好的方法可以更像电子表格单元格,首先进行评估然后单独访问(传播评估)?
这当然也适用于多维数组。
答案 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的角度来看,这是一个很好的例子,它可以创建一个通用的抽象类来实现常见的行为,并为该类的每个“风味”提供许多小的,特定的实现。