如何在C#中动态设置数组长度

31

我还不熟悉C#,在使用数组时遇到了一些问题。我有一个元数据对象(名称-值对)的数组,我想知道如何只创建我真正需要的“InputProperty”对象数量。在这个循环中,我任意地将元素数设置为20,并在条目变为空时尝试退出,但接收此内容的Web服务不允许传递任何空元素:

private Update BuildMetaData(MetaData[] nvPairs)
{
    Update update = new Update();
    InputProperty[] ip = new InputProperty[20];  // how to make this "dynamic"
    int i;
    for (i = 0; i < nvPairs.Length; i++)
    {
        if (nvPairs[i] == null) break;
        ip[i] = new InputProperty();
        ip[i].Name = "udf:" + nvPairs[i].Name;
        ip[i].Val = nvPairs[i].Value;
    }
    update.Items = ip;
    return update;
}

总之,如果上述输入数组中只有3个名称值对,我该怎么办?而不是为名为ip的数组分配20个元素,如何编写代码使得ip只需要尽可能小。更新对象将通过另一个web服务传递,因此序列化非常重要(即不能使用namevaluecollection等)。

附:唯一跟进已发布问题的方式是通过“添加评论”功能吗?

9个回答

34
InputProperty[] ip = new InputProperty[nvPairs.Length]; 

或者,您可以像这样使用列表:

List<InputProperty> list = new List<InputProperty>();
InputProperty ip = new (..);
list.Add(ip);
update.items = list.ToArray();

我想指出的另一件事,在C#中,您可以在for循环内部直接声明使用int变量:

for(int i = 0; i<nvPairs.Length;i++
{
.
.
}

我想分享一种更为简洁的实现方式:

private Update BuildMetaData(MetaData[] nvPairs)
{
        Update update = new Update();
        var ip = new List<InputProperty>();

        foreach(var nvPair in nvPairs)
        {
            if (nvPair == null) break;
            var inputProp = new InputProperty
            {
               Name = "udf:" + nvPair.Name,
               Val = nvPair.Value
            };
            ip.Add(inputProp);
        }
        update.Items = ip.ToArray();
        return update;
}

我认为你在这里忽略了问题——输入数组nvPairs在有用的值之后包含了空值。仅仅使用它的长度并不能解决任何问题。 - Whatsit
OP从未指定。我只是假设如果空是一个简单的合理性检查。如果你说的确实是这种情况,那么你是对的。列表绝对是正确的选择。 - BFree
嗯,我没有考虑到它可能是一个健全性检查。这当然是可能的。 - Whatsit
Whatsit是正确的(输入数组nvPairs包含有用条目之后的空条目。我尝试了BFree上面提供的“清理”方法,稍作修改以避免编译时错误。 - John Adams
如果名为ip的数组分配了与nvPairs相同的长度,那么上面的代码如何将数组ip填充为对象。我看到在foreach循环内创建了一个InputProperty对象,但我不知道这些对象是如何添加到数组ip中的。 - John Adams
好的,我编辑了我的帖子。现在它正在使用一个列表,并正确地添加新的InputProperty对象。我直接在答案框中输入代码,所以如果有语法错误,我很抱歉。 - BFree

24

如果你不想使用ListArrayList或其他动态大小的集合然后转换为数组(顺便说一下,这是我推荐的方法),那么你就必须分配数组到其可能的最大大小,跟踪您放入其中的项目数量,然后创建一个新的只包含这些项目的数组:

private Update BuildMetaData(MetaData[] nvPairs)
{
    Update update = new Update();
    InputProperty[] ip = new InputProperty[20];  // how to make this "dynamic"
    int i;
    for (i = 0; i < nvPairs.Length; i++)
    {
        if (nvPairs[i] == null) break;
        ip[i] = new InputProperty(); 
        ip[i].Name = "udf:" + nvPairs[i].Name;
        ip[i].Val = nvPairs[i].Value;
    }
    if (i < nvPairs.Length)
    {
        // Create new, smaller, array to hold the items we processed.
        update.Items = new InputProperty[i];
        Array.Copy(ip, update.Items, i);
    }
    else
    {
        update.Items = ip;
    }
    return update;
}

另一种方法是始终将 update.Items = ip; 分配,并在必要时进行调整大小:

update.Items = ip;
if (i < nvPairs.Length)
{
    Array.Resize(update.Items, i);
}

这样做的代码量更少,但很可能会完成相同的工作(即创建一个新数组并复制旧项)。


8
请使用以下内容:
 Array.Resize(ref myArr, myArr.Length + 5);

5

需要使用数组吗?如果您使用C#中提供的ArrayList或其他对象,则不会有这种限制。Hashtable、IDictionary、IList等都允许动态数量的元素。


我同意。如果您需要一个具有动态长度的数组功能,那么ArrayList是最简单和明显的选择。 - TheTXI
ArrayList?我们对泛型过敏吗? - Jim Mischel
michl86评论说,在结尾处使用List和.ToArray太慢了。看起来ArrayList可以以同样的方式转换为数组。对于我的最终结果,我需要一个数组而不是ArrayList。在返回之前使用.ToArray()性能真的那么差吗? - John Adams
哈哈 - 通用类型也很酷。我试图尽可能地保持与他使用的内容一致。 - Brian
我认为@michl86所说的“太慢”并不是指性能,而是指@BFree比他更快地回答了问题。 - Michael Meadows
显示剩余2条评论

2
你可以在方法内部使用List,然后在最后将其转换为数组。但我认为如果我们谈论的是20作为最大值,那么你的代码会更快。
    private Update BuildMetaData(MetaData[] nvPairs)
    {
        Update update = new Update();
        List<InputProperty> ip = new List<InputProperty>();
        for (int i = 0; i < nvPairs.Length; i++)
        {
            if (nvPairs[i] == null) break;
            ip[i] = new InputProperty();
            ip[i].Name = "udf:" + nvPairs[i].Name;
            ip[i].Val = nvPairs[i].Value;
        }
        update.Items = ip.ToArray();
        return update;
    }

改编上述代码。 出现“找不到类型或命名空间名称'List'(您是否缺少使用指令或程序集引用?)”的问题。 我也有这个问题(他还想要什么): 使用System.Collections; 使用System.Collections.Generic; 使用System.Collections.Specialized; - John Adams

2

在C# 3.0中使用System.Linq可以跳过中间列表:

private Update BuildMetaData(MetaData[] nvPairs)
{
        Update update = new Update();
        var ip = from nv in nvPairs
                 select new InputProperty()
                 {
                     Name = "udf:" + nv.Name,
                     Val = nv.Value
                 };
        update.Items = ip.ToArray();
        return update;
}

你忘记检查 nv 是否为空。 - Baptiste Pernet

2
使用Array.CreateInstance动态创建数组。
    private Update BuildMetaData(MetaData[] nvPairs)
    {
        Update update = new Update();
        InputProperty[] ip = Array.CreateInstance(typeof(InputProperty), nvPairs.Count()) as InputProperty[];
        int i;
        for (i = 0; i < nvPairs.Length; i++)
        {
            if (nvPairs[i] == null) break;
            ip[i] = new InputProperty();
            ip[i].Name = "udf:" + nvPairs[i].Name;
            ip[i].Val = nvPairs[i].Value;
        }
        update.Items = ip;
        return update;
    }

0
通常,数组需要常量来初始化其大小。您可以扫描nvPairs一次以获取长度,然后使用变量创建一个“动态”数组,例如使用以下方法:
InputProperty[] ip = (InputProperty[])Array.CreateInstance(typeof(InputProperty), length);

我不会推荐这样做,不过还是坚持使用原来的。

List<InputProperty> ip = ...
...
update.Items = ip.ToArray();

解决方案。它的性能损失并不是很大,而且外观更好。


0

你可以通过以下方式动态创建数组:

 static void Main()
    {
        // Create a string array 2 elements in length:
        int arrayLength = 2;
        Array dynamicArray = Array.CreateInstance(typeof(int), arrayLength);
        dynamicArray.SetValue(234, 0);                              //  → a[0] = 234;
        dynamicArray.SetValue(444, 1);                              //  → a[1] = 444;
        int number = (int)dynamicArray.GetValue(0);                      //  → number = a[0];


        int[] cSharpArray = (int[])dynamicArray;
        int s2 = cSharpArray[0];

    }

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