C# 数组初始化 - 使用非默认值

33

你知道的在C#中初始化动态大小的数组最简便的方式是什么?

这是我能想到的最好方法。

private bool[] GetPageNumbersToLink(IPagedResult result)
{
   if (result.TotalPages <= 9)
      return new bool[result.TotalPages + 1].Select(b => true).ToArray();

   ...
7个回答

74
如果你所说的“slickest”是指最快的话,那么很遗憾,Enumerable.Repeat可能比for循环慢20倍。 请参考http://dotnetperls.com/initialize-array
Initialize with for loop:             85 ms  [much faster]
Initialize with Enumerable.Repeat:  1645 ms 

那就使用Dotnetguy的SetAllValues()方法。


38

12
我认为Nigel的表现注释值得一提 - https://dev59.com/LnVC5IYBdhLWcg3w9GE9#1051227 - CrimsonX
11
我不敢相信人们会为一些在我看来又“模糊”又昂贵的操作投票赞同,而这个操作其实非常简单,只需填充一个数组。var arr = new type[10]; for (int i = 0; i < arr.Length; i++) arr[i] = value;,这是76个字符的代码,每个人都能理解。相比之下,有67个字符的模糊操作。 - Aidiakapi

14

编辑:如评论者指出,我的原始实现不起作用。这个版本可以工作,但基于for循环的方式相对来说不太流畅。

如果您愿意创建一个扩展方法,您可以尝试使用以下代码:

public static T[] SetAllValues<T>(this T[] array, T value) where T : struct
{
    for (int i = 0; i < array.Length; i++)
        array[i] = value;

    return array;
}

然后像这样调用它。
bool[] tenTrueBoolsInAnArray = new bool[10].SetAllValues(true);

作为另一种选择,如果你乐意保留一个类,你可以尝试像这样的方法。
public static class ArrayOf<T>
{
    public static T[] Create(int size, T initialValue)
    {
        T[] array = (T[])Array.CreateInstance(typeof(T), size);
        for (int i = 0; i < array.Length; i++)
            array[i] = initialValue;
        return array;
    }
}

您可以像调用以下代码一样调用它:

bool[] tenTrueBoolsInAnArray = ArrayOf<bool>.Create(10, true);

我不确定我更喜欢哪种,虽然一般来说我非常喜欢扩展方法。


我不相信你的SetAllValues会起作用:在你的lambda表达式中,x没有通过引用传递,所以给它赋值并不会改变存储在数组中的值。 - Samuel Jack
是的,你说得完全正确。我提到我实际上还没有编译它,否则就会显示出那个非常基本的错误。我用一个简单的循环替换了ForEach<T>,这样可以正常工作,但不如提问者要求的那么流畅。 - Neil Hewitt
1
除此之外,您的扩展方法具有暗示它将返回一个新数组的签名,但它修改了原始数组并返回该数组。这是不好的形式。 - Robert Jeppesen
一、将您的“SetAllValues”命名为“Fill”,我觉得这是一个更好的名称。 二、在第二种情况下,利用C#的类型推断。因此,可以进行以下声明:ArrayEx.Create(10, true); - nawfal
你不需要使用 Array.CreateInstance,只需使用 new T[] - Ohad Schneider

5
我建议这样做:

实际上,我建议:

return Enumerable.Range(0, count).Select(x => true).ToArray();

这种方法只需要分配一个数组。本质上,这是一种更简洁的表达方式:

var array = new bool[count];

for(var i = 0; i < count; i++) {
   array[i] = true;
}

return array;

1
甚至可以使用新的bool[count].Select(x=>true).ToArray()。 - BjartN
2
那仍然会分配两个数组。 - user22467

1
许多时候,您可能希望使用不同的值初始化不同的单元格:
public static void Init<T>(this T[] arr, Func<int, T> factory)
{
    for (int i = 0; i < arr.Length; i++)
    {
        arr[i] = factory(i);
    }
}

或者以工厂的方式:

public static T[] GenerateInitializedArray<T>(int size, Func<int, T> factory)
{
    var arr = new T[size];
    for (int i = 0; i < arr.Length; i++)
    {
        arr[i] = factory(i);
    }
    return arr;
}

1

在撰写本答案时,所有答案都忽略了.NET明确为此提供的功能,引用.NET代码注释,他们的“优化工作方法”适用于所有大小不为1字节的类型。如果只有一个字节,则将调用memset。

如果您对其如何工作感兴趣,则工作方法涉及将矢量化和循环展开相结合,以使用跨度设置保证连续的内存区域中的所有元素为给定值。该保证允许进行更进一步的构建和JIT优化。 要矢量化此过程,有四个约束条件:

  1. 架构必须支持SIMD指令,
  2. 类型不能是或包含引用,
  3. 类型的大小不能大于字节向量中的车道数,
  4. 类型的大小必须是2的幂。

要利用此功能,您只需要调用Array.Fill或跨度的Fill方法即可。以下是一个示例:

var a = new bool[1_000];
Array.Fill(a, true); // Same as: a.AsSpan().Fill(true);
Console.WriteLine(String.Join(", ", a));

0

未经测试,但你能试着这样做吗?

return result.Select(p => true).ToArray();

跳过 "new bool[]" 部分?


1
只有当 IPagedResult : IEnumerable<T> 时。 - Mark Cidade

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接