简体   繁体   English

有没有办法在C#中内联委托?

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

Here's a simplified scenario that I'm dealing with. 这是我正在处理的简化场景。 There are multiple methods with the looping structure. 循环结构有多种方法。

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

In one method, Update(a,b,c) is 在一种方法中,Update(a,b,c)是

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

In another method, it is 在另一种方法中,它是

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

And yet in another method, it is 然而在另一种方法中,它是

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

At the moment, my code is duplicated everywhere. 目前,我的代码到处都是重复的。 Is there a pattern in C# so that I don't duplicate code? C#中是否有一个模式,以便我不重复代码? I was tempted to use delegate but it seems that delegate would degrade the performance (which is critical in my case). 我很想使用委托,但似乎委托会降低性能(这在我的情况下是至关重要的)。

Is there a way to write a macro or an inline delegate function for such scenario? 有没有办法为这种情况编写宏或内联委托函数?

Something like this? 像这样的东西?

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

and then in the caller 然后在来电者

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

Is that what you're looking for? 这就是你要找的东西吗?

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

Usage: 用法:

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

I see that a is always accessed by i (and so on - b by j , c by k ). 我看到a总是被i访问(依此类推 - b jc k )。 You can try to optimize the code using this fact. 您可以尝试使用此事实优化代码。

If performance is critical you could avoid the method call in the inner loop, like this: 如果性能至关重要,您可以避免内部循环中的方法调用,如下所示:

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

and the calling code: 和调用代码:

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

Likely you are implementing something like large number multiplication or other linear combination of vectors. 你可能正在实现像大数乘法或其他向量线性组合这样的东西。 The reason you would need the approach you described as inline delegate is quite probably because of the varying place to store the result during the calculation and also because the nested for-loops are hard coded. 你需要你所描述的内联委托方法的原因很可能是因为在计算过程中存储结果的位置不同,并且因为嵌套的for循环是硬编码的。 Thus I suggest to revise your code like following: 因此,我建议修改您的代码,如下所示:

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

This code is implemented in a recursive manner. 此代码以递归方式实现。 The int[][] array can be replace with a generic array as long as you are going to define the calculation of operator * and operator + , rather the methods name in MutiplyScalar and AddScalar . 只要您要定义operator *operator +的计算,而不是MutiplyScalarAddScalar的方法名称, int[][] array可以替换为泛型数组。

So, we would not use a delegate of Update to control the destination. 因此,我们不会使用Update的委托来控制目标。 Instead, we just use the destinationIndex to accomplish that. 相反,我们只使用destinationIndex来实现它。 Following is a test case: 以下是测试用例:

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

We still have inline delegates there, which are called Lambda Expressions in c#. 我们仍然有内联代理,在c#中称为Lambda Expressions According to the original code you provided, there're Do something s between the nested for-loops. 根据您提供的原始代码,在嵌套的for循环之间Do something However, there is not much information we can found which is non-globally known to Update ; 然而,没有太多的信息,我们可以发现,这是不可全球知名的以Update ; the most significant difference we can see is the iterating index and the ending number , which are i, I , j, J and k, K . 我们可以看到的最显着的差异是迭代索引和结束数 ,它们是i, Ij, Jk, K Thus we just take these as arguments to pass to the Action<int, int> for doing something, and they are variable for each level of for-loop. 因此,我们只需将这些作为参数传递给Action<int, int>以执行某些操作,并且它们对于每个for-loop级别都是可变的。

The execution is much depended on the indices . 执行很大程度上取决于indices It stores the iterating index of current for-loop and passed to next level of recursive call. 它存储当前for循环的迭代索引,并传递给下一级递归调用。 Further if you passed the arrays with a count smaller than its Length in indices , it's treated as an array with the length of that count you passed to. 另外如果通过了arrays具有比其小的计数Lengthindices ,它会被视为与您传递给计数的长度的数组。 Don't pass a negative count, neither a larger one. 不要传递负数,也不要传递更大的数。 It can be lack of Action<int, int> , that would just mean do nothing instead of do something . 它可能缺少Action<int, int> ,这只是意味着什么都不做而不是做某事

This will probably inline it. 这可能会内联它。

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