【问题标题】:C# Passing a pointer of array to function starting at specific offsetC#将数组指针传递给从特定偏移量开始的函数
【发布时间】:2014-12-03 18:00:50
【问题描述】:

我正在将 C++ 应用程序移植到 C# 并遇到一些指针问题。

我想要实现的是传递一个带有偏移量的数组指针,以便传递的函数可以在数组的正确部分上工作。我不想更改函数的签名来为偏移量添加额外的值。

所以,这是我想在 C# 中传递的一段 C++ 代码示例:

void DoSomething( double p[] )
{
    p[0] = 0.4;
    p[1] = 0.4;
}

int main()
{
    double Vector[3];

    Vector[0] = 0.2;
    Vector[1] = 0.2;
    Vector[2] = 0.2;

    DoSomething( &Vector[1] );
}

我怎么能这样做?记住我不能通过偏移量?

[编辑] 谢谢大家的回答。 首先,我必须道歉:我在复制代码时犯了一个大错误。

我写的

DoSomething( Vector[1] );

在最后一行而不是

DoSomething( &Vector[1] );

此问题已得到纠正。

然后我意识到我对签名不是很清楚。

  • 我可以稍微改变函数的签名,但我不能添加任何参数

  • 我已经在使用“不安全”和“固定”关键字,所以不会伤害到我

  • 它不需要是高效的代码,因为此移植旨在快速实现其他人为原型项目编写的算法。如果项目是“好的”,代码将被扔进垃圾箱并在 C++ dll 中重写。

  • 函数“DoSomething”实际上是几个其他函数的嵌套,它被设计为一个快速的数学工作,但遗憾的是,我没有自己编写代码的所有知识。这就是为什么我认为作者已经很好地设计了它的功能,因为它已在全球范围内使用。

我会尝试 Servy 的建议,并在几天后回复您。

【问题讨论】:

  • 这也是相当糟糕的 C++ 代码。我自己会使用枚举器。
  • 我同意 IdeaHat 和 Yakk。可以在 C++ 中编写p[3] = 0.4; DoSomething。你怎么知道数组边界?
  • 您可以像这样使用 LINQ:var filters = Vector.GetRange(index,count).... 将 Vector 数组更改为 List
  • @JohnPeters - 重要的是要指出这会创建一个新数组。系统希望更改数组中的元素,因此这在功能上不起作用。
  • 在不改变被调用方法的签名的情况下,没有实际的方法可以实现这一点。你为什么反对这样做?将偏移量本身推送到需要它的方法有什么问题?

标签: c# arrays pointers porting


【解决方案1】:

不可能完全不更改DoSomething 的签名,但是您可以避免需要同时传递一个数组并且它在整个地方并排偏移。您可以通过创建一个组成数组并跟踪偏移量的类来做到这一点:

public class ArrayReference<T> : IEnumerable<T>
{
    //you can keep these entirely private if you prefer
    public T[] Array { get; private set; }
    public int Offset { get; private set; }
    public ArrayReference(T[] array, int offset)
    {
        Array = array;
        Offset = offset;
    }

    public T this[int index]
    {
        get
        {
            return Array[index + Offset];
        }
        set
        {
            Array[index + Offset] = value;
        }
    }

    public int Length
    {
        get
        {
            return Array.Length - Offset;
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        for (int i = Offset; i < Array.Length; i++)
            yield return Array[i];
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public static ArrayReference<T> operator +(
        ArrayReference<T> reference, int offset)
    {
        return new ArrayReference<T>(reference.Array, reference.Offset + offset);
    }

    public static ArrayReference<T> operator -(
        ArrayReference<T> reference, int offset)
    {
        return new ArrayReference<T>(reference.Array, reference.Offset - offset);
    }

    public static implicit operator ArrayReference<T>(T[] array)
    {
        return new ArrayReference<T>(array, 0);
    }
    public static implicit operator T[](ArrayReference<T> reference)
    {
        return reference.ToArray();
    }
}

您可能希望根据您的特定需求为此添加其他功能。您可以根据需要/想要公开尽可能多或尽可能少的底层数组功能。

【讨论】:

  • 是的,创建一个新的对象类型是解决这个问题的 C# 方法。我个人认为更改DoSomething() 函数会更容易。但对每个人来说都是他自己的。
  • 请注意,这会改变方法的签名,就像传递偏移量一样。
  • @Hogan 对于这个非常简单的示例,传递数组的偏移量可能很简单,当然,但是在一个非常复杂的程序中,并且处理大量不断变化的数组更改偏移量时,将所有用于管理具有偏移量的数组的逻辑的逻辑包装到一个类中实际上是合理的。 OP 的真正的问题是否复杂到足以保证这是他需要自己做出的决定。
  • @PeterDuniho 确实如此。 不可能不以某种方式更改签名并具有所描述的行为。没有办法创建一个相同类型的 actual 数组,它只是对另一个数组的引用。在不更改签名的情况下,您可以做的最好的事情是复制数组,然后在调用 DoSomething 后将所有数据复制回来。
  • @Servy - 我同意,这就是为什么我给了你我所有的赞成票。我很困惑为什么有人会投反对票。
【解决方案2】:

这实际上不是你在 C# 中的做法。

这样做的唯一方法是使用不安全的代码,即使这样也不是一个好的实现,因为你的方法是不安全的,你的数组必须是fixed

fixed 关键字会阻止垃圾收集器将您的数组移动到内存中的另一个位置,但它可能会导致内存分区,进而导致性能下降。

此外,即使按照设计,这也不是一件好事,因为您不知道方法中的数组边界。

但如果您真的想这样做,请使用枚举器。

在你的主要方法中:

double[] d = new double[3];
d[0] = 1.0;
d[1] = 2.0;
d[2] = 3.0;

IEnumerator<double> e = d.AsEnumerable().GetEnumerator();
e.MoveNext();
tryEnumerate(e);

然后是你的DoSomething 方法:

static void DoSomething(IEnumerator<double> e)
{
    while(e.MoveNext())
        Console.WriteLine(e.Current.ToString());
}

【讨论】:

  • 首先,您真的希望在这里使用IEnumerable,而不是IEnumerator,其次,这将是数据结构的只读视图;他的意图是改变数据。
【解决方案3】:

更新代码以显示使用扩展方法的链接。您可以轻松返回任何和所有更改并更新任何想要的内容。

List<double> Vector;
Vector.Add(0.2);
Vector.Add(0.2);
Vector.Add(0.2);
DoSomething(Vector.GetRange(index,count));

如果您需要维护原始列表,请执行以下操作:

public static List<double> GetRange(static List<double> list, int index, int count){
     return list.GetRange(Index,Count);
}
public static List<double> DoSomething(static List<double> list){
     //do something here
}

像这样使用它:

OriginalList().GetRange(Index,Count).DoSomething();

【讨论】:

  • 这不起作用 DoSomething 会更改数组的元素 -- 这只会更改副本。
  • 这正在创建列表的副本。对该副本的更改不会影响原始列表。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-22
  • 1970-01-01
  • 2013-02-15
相关资源
最近更新 更多