【问题标题】:Passing by reference to n-th element in C#通过引用传递 C# 中的第 n 个元素
【发布时间】:2016-04-10 01:54:45
【问题描述】:

在 C 中,如果我们有一个数组,我们可以通过引用将它传递给一个函数。我们还可以使用简单的添加(n-1) 来传递从数组的第n 元素开始的引用,如下所示:

char *strArr[5];
char *str1 = "I want that!\n";
char *str2 = "I want this!\n";
char *str3 = "I want those!\n";
char *str4 = "I want these!\n";
char *str5 = "I want them!\n";
strArr[0] = str1;
strArr[1] = str2;
strArr[2] = str3;
strArr[3] = str4;
strArr[4] = str5;
printPartially(strArr + 1, 4); //we can pass like this to start printing from 2nd element

....

void printPartially(char** strArrPart, char size){
int i;
for (i = 0; i < size; ++i)
    printf(strArrPart[i]);
}

导致这些:

I want this!
I want those!
I want these!
I want them!

Process returned 0 (0x0)   execution time : 0.006 s
Press any key to continue.

在 C# 中,我们还可以通过 ref(或 out)传递对 object 的引用。该对象包括数组,它是 whole 数组(或者至少,我认为它是这样工作的)。但是我们如何通过引用数组的n-th 元素来传递,这样在函数内部,只有string[] 的元素比原始string[] 少一个,而无需创建新数组?

我们必须使用unsafe吗?我正在寻找没有unsafe的解决方案(如果可能)

编辑:

我知道我们可以在没有 ref 关键字的情况下在 C# 中传递 Array。当我们谈论Array 时提到ref,也许我的问题听起来很误导。为什么我把ref放在那里,我宁愿这样说:是ref关键字可以用来传递对数组的n-th元素的引用,就像C一样而不是传递对任何object 的引用(不提及n-th 元素或类似的东西)?我对问题的措辞表示对任何误解的道歉。

【问题讨论】:

  • 您可以将数组和起始索引传递给它。该代码在C 中工作,因为(至少在这种情况下)数组和指针是可互换的。在C# 中并非如此(不管它是如何在幕后实现的)。您应该将数组视为任何其他有序集合。
  • @Rob 是的,这是可能的解决方案,先生。你是对的,并不是我没有想到这种可能性(指传递起始索引),但我正在寻找一种替代方案,如果可能的话,就像我们本能地看到一个数组应该从索引 0 开始(如 C 示例),我们也可以在 C# 函数/方法中这样做。
  • 似乎过早的优化? C# 会为你的文字字符串做字符串实习,所以我认为它已经被优化了。
  • @Rob 我的意思是,毕竟 C# 有 ref keyword。我想知道我们是否可以用它做更多我可能不知道的事情。
  • @JesseGood “文字字符串的字符串实习” 我不知道有这样的功能,先生。它是什么,有什么作用?

标签: c# arrays pass-by-reference


【解决方案1】:

问题很旧,但也许答案对某人有用。 从 C# 7.2 开始,在这种情况下可以使用更多类型,例如。 SpanMemory.
它们完全允许您在问题中提到的内容(以及更多内容)。
Here's great article about them

目前,如果您想使用它们,请记住在项目的 .csproj 文件中添加 &lt;LangVersion&gt;7.2&lt;/LangVersion&gt; 以使用 C# 7.2 功能

【讨论】:

    【解决方案2】:

    编辑:

    您将无法像在 C 中的 安全 代码中那样做到这一点。

    C# 数组(即string[])派生自抽象类型Array。 它不仅仅是 C 中的简单内存块。

    所以你不能发送其中一个元素的引用并从那里开始迭代。


    但是有一些解决方案当然会给你同样的味道(没有不安全):

    喜欢:

    • 正如@Chris 所说,您可以使用ArraySegment&lt;T&gt;
    • 由于Array 也是IEnumerable&lt;T&gt;,您可以使用.Skip 并发送返回值。 (但这会给你一个IEnumerable&lt;T&gt; 而不是Array)。但它可以让你进行迭代。
    • 等等……

    【讨论】:

    • 我知道我们可以在没有 ref 关键字的情况下在 C# 中做到这一点。也许我的问题误导了一些人。也许我宁愿这样说:是否可以使用ref 关键字,比如像 C 一样传递对数组的第 n 个元素的引用?
    • @Ian 我编辑了我的答案以纠正我的误解。
    【解决方案3】:

    您的困惑很常见。关键是要意识到“引用类型”和“通过引用传递”(参考键盘)是完全独立的。在这种特定情况下,由于 string[] 是引用类型(所有数组也是),这意味着当您传递对象时不会复制对象,因此您总是引用同一个对象。

    C#代码修改版:

    string[] strArr = new string[5];
    strArr[0] = "I want that!\n";
    strArr[1] = "I want this!\n";
    strArr[2] = "I want those!\n";
    strArr[3] = "I want these!\n";
    strArr[4] = "I want them!\n";
    printPartially(strArr.Skip(1).Take(4).ToArray());
    
    void printPartially(string[] strArr)
    {
        foreach (string str in strArr)
        {
            Console.WriteLine(str);
        }
    }
    

    【讨论】:

    • 但是他不想传递整个数组,他想传递对数组固定位置的引用。这不会那样做。
    • 您现在没有传递对数组中项目的引用,而是将它们复制到新数组,因此对副本的更改不会影响原始数据。
    【解决方案4】:

    如果方法应该只从数组中读取,你可以使用 linq:

    string[] strings = {"str1", "str2", "str3", ...."str10"};
    print(strings.Skip(1).Take(4).ToArray());
    

    【讨论】:

      【解决方案5】:

      “安全”的方法是传递 ArraySegment 结构。

      您当然可以使用不安全的 c# 传递指向字符的指针,但是您需要担心缓冲区溢出。

      顺便说一句,C# 中的数组(通常)是在堆上分配的,因此正常传递它(没有引用)并不意味着复制数组——它仍然是一个被传递的引用(只是一个新的)。

      【讨论】:

      • 感谢您的链接,先生。会看看那个。不过,如果您能对它如何与某些代码一起工作提供更多解释,那将是最好的。
      • @Ian 您可以通过查看源代码轻松了解它是如何工作的 - referencesource.microsoft.com/#mscorlib/system/… (代码中也没有任何重要内容 - 就像任何人都会做的一样,只显示视图数组的 - 将索引重定向到原始数组之前的开始和边界检查的偏移量)。如果您需要更多解释 - 考虑提出单独的问题。
      • @AlexeiLevenkov 感谢您提供的材料。会检查的!
      猜你喜欢
      • 2016-04-09
      • 2020-05-28
      • 1970-01-01
      • 2017-04-18
      • 1970-01-01
      • 1970-01-01
      • 2011-01-17
      • 1970-01-01
      • 2020-04-01
      相关资源
      最近更新 更多