簡體   English   中英

有沒有辦法在C#中內聯委托?

[英]Is there a way to inline delegate in C#?

這是我正在處理的簡化場景。 循環結構有多種方法。

for (int i=0; i<I; i++) {
    // Do something
    for (int j=0; j<J; j++) {
        // Do something
        for (int k=0; k<K; k++) {
            // Do something
            Update(a,b,c);
        }
    }
}

在一種方法中,Update(a,b,c)是

a[i] += b[j] * c[k]

在另一種方法中,它是

b[j] += a[i] * c[k]

然而在另一種方法中,它是

c[k] += a[i] * b[j]

目前,我的代碼到處都是重復的。 C#中是否有一個模式,以便我不重復代碼? 我很想使用委托,但似乎委托會降低性能(這在我的情況下是至關重要的)。

有沒有辦法為這種情況編寫宏或內聯委托函數?

像這樣的東西?

void DoUpdates(Action<int, int, int> update)
{
  for (int i=0; i<I; i++) {
    // Do something
    for (int j=0; j<J; j++) {
      // Do something
      for (int k=0; k<K; k++) {
        // Do something
        update(i, j, k);
      }
    }
  }
}

然后在來電者

DoUpdates((int i, int j, int k) => { a[i] += b[j] * c[k]; });

這就是你要找的東西嗎?

void Update<T>(T[] x, T[] y, T[] z, int i, int j, int k)
{
    x[i] += y[j] * z[k];
}

用法:

Update(a, b, c, i, j, k);
Update(b, a, c, j, i, k);
Update(c, a, b, k, i, j);

我看到a總是被i訪問(依此類推 - b jc k )。 您可以嘗試使用此事實優化代碼。

如果性能至關重要,您可以避免內部循環中的方法調用,如下所示:

void Update(int[]x, int[]y, int[]z, int I, int J, int K)
{
    for (int i = 0; i < I; i++)
    {
        // Do something
        for (int j = 0; j < J; j++)
        {
            // Do something
            for (int k = 0; k < K; k++)
            {
                // Do something
                x[i] += y[j] * z[k];
            }
        }
    }
}

和調用代碼:

Update(a, b, c, I, J, K);
Update(b, a, c, J, I, K);
Update(c, a, b, K, I, J);

你可能正在實現像大數乘法或其他向量線性組合這樣的東西。 你需要你所描述的內聯委托方法的原因很可能是因為在計算過程中存儲結果的位置不同,並且因為嵌套的for循環是硬編碼的。 因此,我建議修改您的代碼,如下所示:

public void Update(int destinationIndex, int[][] arrays, int[] indices) {
    var product=1;

    for(var i=indices.Length; i-->0; )
        if(destinationIndex!=i)
            product*=arrays[i][indices[i]];

    arrays[destinationIndex][indices[destinationIndex]]+=product;
}

public void PerformUpdate(
    int destinationIndex, int[] counts, int[][] arrays, Action<int, int>[] actions,
    List<int> indices=null, int level=0
    ) {
    if(level==counts.Length)
        Update(destinationIndex, arrays, (indices??new List<int>()).ToArray());
    else
        for(int count=counts[level], i=0; i<count; i++) {
            if(null!=actions&&level<actions.Length)
                actions[level](i, count); // do something according to nesting level

            (indices=indices??new List<int>()).Add(i);
            PerformUpdate(destinationIndex, counts, arrays, actions, indices, 1+level);
            indices.RemoveAt(indices.Count-1);
        }
}

此代碼以遞歸方式實現。 只要您要定義operator *operator +的計算,而不是MutiplyScalarAddScalar的方法名稱, int[][] array可以替換為泛型數組。

因此,我們不會使用Update的委托來控制目標。 相反,我們只使用destinationIndex來實現它。 以下是測試用例:

int[] a=new[] { 1, 2 }, b=new[] { 3, 4, 5 }, c=new[] { 6 };
Action<int, int> m=(index, count) => Debug.Print("index={0}; count={1}", index, count);
PerformUpdate(2, new[] { a.Length, b.Length, c.Length }, new[] { a, b, c }, new[] { m, m, m });

我們仍然有內聯代理,在c#中稱為Lambda Expressions 根據您提供的原始代碼,在嵌套的for循環之間Do something 然而,沒有太多的信息,我們可以發現,這是不可全球知名的以Update ; 我們可以看到的最顯着的差異是迭代索引和結束數 ,它們是i, Ij, Jk, K 因此,我們只需將這些作為參數傳遞給Action<int, int>以執行某些操作,並且它們對於每個for-loop級別都是可變的。

執行很大程度上取決於indices 它存儲當前for循環的迭代索引,並傳遞給下一級遞歸調用。 另外如果通過了arrays具有比其小的計數Lengthindices ,它會被視為與您傳遞給計數的長度的數組。 不要傳遞負數,也不要傳遞更大的數。 它可能缺少Action<int, int> ,這只是意味着什么都不做而不是做某事

這可能會內聯它。

interface IFunc<T>
{
    void Invoke(ref T a, ref T b, ref T c);
}

void DoUpdates<TFunc>(TFunc update)
    where TFunc : IFunc<int>
{
    for (int i = 0; i < I; i++)
        for (int j = 0; j < J; j++)
            for (int k = 0; k < K; k++)
                update.Invoke(ref i, ref j, ref k);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM