1

例 1

私たちが知っているように、データベースには、隣接する列の値に基づいて列が計算される計算列の概念があります。

問題は、計算された列が現在の行以外の行に関連付けられないことです。

例 2

次に、セルに数式を入れることができるスプレッドシート (つまり、Excel) があります。これは計算列に似ていますが、より強力です。数式は、RDB のように現在の行/列だけでなく、スプレッドシートの任意のセル (またはそれらのセット) に関連付けることができます。

問題

計算された (または自動更新された値) の概念は素晴らしいですが、C# の一連の値/オブジェクトで同様のことを行うにはどうすればよいでしょうか?

それぞれが同じリスト内の他の値 (またはセット) に関連付けられている値のリスト (または配列) を作成したいと思いますか? これは、スプレッドシートのセル値のようなものです...値を変更すると、関連する値が変更されます(関連するサブツリーの値全体も同様に変更されます)。

C# にそのような概念はありますか?

最も賢い方法でこれを行うにはどうすればよいでしょうか? LinkedListオブジェクトのプロパティの値が同じリスト内の他のオブジェクトに関連し、値がアクセスされるたびに評価されるオブジェクトを持つことができることを私は知っています(必要に応じて評価)。これは、途中でいくつか (すべての祖先ノード) が評価されることを意味する場合があります。最初に評価されてから個別にアクセスされるスプレッドシートのセルのように機能するより良い方法はありますか (評価の伝播)?

もちろん、これは多次元配列にも適用できます。

4

4 に答える 4

3

この概念は、リアクティブ プログラミングと呼ばれます。.NETには、あなたが説明したことを達成できるReactive Extensionsと呼ばれるものがあります。具体的には、 Behaviorと呼ばれるものを使用する必要があります

于 2011-10-24T09:15:13.283 に答える
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;
        }
    }
}
于 2011-10-24T09:17:18.460 に答える
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];
    }
}

これで、ラムダ式を使用して値を保存できます。

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] );
于 2011-10-24T09:31:17.593 に答える
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;
    }
}

このクラスについて注意すべき点が 1 つあります。その動作方法のため、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 の場合はこれが必要です)。修正します。

あとは、expression から継承するすべての子クラスを実装するだけです。

これを行うためのより良い方法があるかもしれませんが、オブジェクト指向の観点からは、これは、一般的な動作を実装し、そのクラスの「フレーバー」ごとに多くの小さな特定の実装を持つ汎用抽象クラスを作成する良い例です。

于 2011-10-24T10:38:01.263 に答える