繁体   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