Best way to “push” into C# array
早上好所有
所以我知道这是一个讨论比较广泛的问题,但是我似乎找不到确切的答案。我知道您可以使用列表执行我要的操作,但这不能解决我的问题。
我用初学者C#授课。我有PHP和Java Script的背景,因此倾向于从这些语言方面进行思考。
我一直在寻找向学生展示如何快速向数组中添加元素的最佳方法。我们不能使用列表,因为这些都不是我目前所教课程的一部分(尽管他们确实在请在学期晚些时候来。)
因此,我正在寻找执行Java array.push(newValue)类型场景的最简单方法,这对于新手程序员而言是可以理解的,而无需教他们不好的实践。
到目前为止,这是我一直在解决该问题的方式:
1 2 3 4 5 6 7 8 | for (int i = 0; i < myArray.Length; i++) { if(myArray[i] == null) { myArray[i] = newValue; break; } } |
我只需要知道这是否是可以接受的方法,是否在教给他们任何不良做法,或者是否有更好/更简单的方法来实现自己的目标。
编辑
问题的症结在于需要将元素添加到数组的第一个空插槽中,而这正是Java push函数可以做到的。
任何建议将不胜感激。
1 2 | Array.Resize(ref myArray, myArray.Length + 1); myArray[myArray.GetUpperBound(0)] = newValue; |
编辑:
对于此问题的编辑,我不确定该答案是否确实适用:
The crux of the matter is that the element needs to be added into the
first empty slot in an array, lie a Java push function would do.
我提供的代码有效地附加了一个元素。如果目标是设置第一个空元素,则可以执行以下操作:
1 2 3 4 5 6 | int index = Array.IndexOf(myArray, null); if (index != -1) { myArray[index] = newValue; } |
编辑:
这里是扩展方法,它封装该逻辑并返回放置值的索引;如果没有空元素,则返回-1。请注意,此方法也适用于值类型,将具有该类型默认值的元素视为空。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public static class ArrayExtensions { public static int Push< T >(this T[] source, T value) { var index = Array.IndexOf(source, default(T)); if (index != -1) { source[index] = value; } return index; } } |
您的问题有点离题了。特别是,您说"该元素需要添加到数组的第一个空插槽中,这是Java推函数可以做到的。"
动词" Push "并不是我所知道的任何语言都可以与Array一起使用的语言,除了JavaScript。我怀疑它只存在于JavaScript中,因为它可能存在(因为JavaScript是一种完全动态的语言)。我很确定这不是故意设计的。
可以用这种效率低下的方式编写C#中的JavaScript样式的Push操作:
1 2 3 4 | int [] myArray = new int [] {1, 2, 3, 4}; var tempList = myArray.ToList(); tempList.Add(5); myArray = tempList.ToArray(); //equiv: myArray.Push(5); |
" Push "用于某些类型的容器,尤其是Stacks,Queues和Deques(它们有两次推送-一次从前面,一次从后面)。我敦促您在对数组的解释中不要将Push作为动词包含在内。它对CS学生的词汇没有任何影响。
在C#中,就像在大多数传统过程语言中一样,数组是包含在固定长度的连续内存块中的单一类型元素的集合。分配数组时,将分配每个数组元素的空间(并且在C#中,这些元素被初始化为该类型的默认值,对于引用类型,其值为null)。
在C#中,引用类型的数组用对象引用填充;值类型的数组填充有该值类型的实例。结果,由4个字符串组成的数组使用与作为应用程序类的4个实例组成的数组相同的内存(因为它们都是引用类型)。但是,包含4个DateTime实例的数组比包含4个短整数的数组要长得多。
在C#中,数组的实例是System.Array(引用类型)的实例。数组具有一些属性和方法(例如Length属性)。否则,您将无法对数组做很多事情:您可以使用数组索引从(或向)单个元素读取(或写入)。类型T的数组也实现
数组是可变的(可以写入数组中的值),但是它们具有固定的长度-不能扩展或缩短。它们是有序的,并且不能重新排列(除非手动手动刷新值)。
C#数组是协变的。如果您要问C#语言设计师,这将是他们最后悔的功能。这是打破C#类型安全性的几种方法之一。考虑以下代码(假设Cat和Dog类继承自Animal):
1 2 3 4 |
该"功能"是由于.NET Framework的初始版本中缺少通用类型和显式协方差/协方差以及复制Java"功能"的冲动的结果。
数组确实出现在许多核心.NET API(例如System.Reflection)中。它们再次出现在这里,因为初始版本不支持通用集合。
通常,有经验的C#程序员不会在他的应用程序中使用很多数组,而是倾向于使用功能更强大的集合,例如
在每个人都掌握了基础知识之后,您可能会考虑在数组讲课中添加新的
最后,LINQ(语言集成查询)为集合引入了许多功能(通过向
顺便说一句:您要教哪种班?在什么级别?
如前所述,
1 2 3 4 5 6 | int[] arr = new int[2]; arr[0] = 1; arr[1] = 2; //without this line we'd get a exception Array.Resize(ref arr, 3); arr[2] = 3; |
关于循环的想法:
初始化数组时,将数组的
元素设置为其默认值。因此,如果要在包含引用类型(其默认值为
但是它不适用于值类型,因为它们是用
C#在这方面与JavaScript有所不同。由于进行了严格的检查,因此您可以定义数组的大小,并且应该了解有关数组的所有信息,例如其边界,最后一项放置在何处以及哪些项未被使用。如果要调整其大小,应该将数组的所有元素复制到一个更大的新元素中。
因此,如果您使用原始数组,则除了保持最后一个空的可用xx之外,别无其他方法,就像您已经在这样做的那样,在该索引处将可用的
但是,如果您希望运行时维护此信息并完全抽象出数组,但仍在下面使用数组,则C#提供了一个名为
它处理所有问题,例如调整数组大小,维护最后可用的索引等。但是,它从您那里提取/封装了此信息。您只使用
要将任何类型的项目推入基础数组末尾的基础数组中,请像下面这样调用
1 2 3 4 | /* you may or may not define a size using a constructor overload */ var arrayList = new ArrayList(); arrayList.Add("Foo"); |
编辑:有关类型限制的说明
就像每种编程语言和运行时一样,C#将堆对象与那些不会出现在堆上并且只会保留在函数的参数堆栈中的对象进行分类。 C#通过名称"值类型"与"引用类型"指出了这一区别。其值在堆栈上的所有事物都称为值类型,而将在堆上的所有事物称为引用类型。这与JavaScript在对象和文字之间的区别大致相似。
您可以将任何东西放入C#的
因此,您可以将任何您想要的内容放入数字常量,字符串常量,组成的类的对象,布尔值,浮点数,双精度型,结构体中。 >
这是因为
然后,当您放置不是对象的东西时,C#将创建一个
例如在JavaScript中,虽然您可以使用数字文字来调用
1 2 3 4 5 6 | // Valid javascript var s = 4.toString(); // Invalid JavaScript code 4.prototype.square = () => 4 * 4; var square = 4.square(); |
就像JavaScript在对
1 2 3 | var arrayList = new ArrayList(); arrayList.Add(4); // The value 4 is boxed into a `new Object()` first and then that new object is inserted as the last element in the `ArrayList`. |
这涉及一定的惩罚,就像JavaScript一样。
在C#中,您可以避免这种损失,因为C#提供了
但是,我从您的问题文本中假定您已经知道C#具有用于强类型项目的通用结构。您的问题是要有一个像JavaScript这样的数据结构,表现出无类型性和弹性的语义,就像JavaScript
从您的问题中还可以清楚地看到,您的兴趣是学术上的,而不是在生产应用程序中使用该结构。
因此,我假设对于生产应用程序,您已经知道通用/强类型数据结构(例如
这可以接受,因为它可以分配给数组。但是,如果您要推送,我很确定在数组中是不可能的。而是可以通过使用Stack,Queue或任何其他数据结构来实现。
实数数组不具有此类功能。但是派生类(例如ArrayList)拥有它。
在C#中没有
1 2 3 4 5 6 7 8 | static void Main() { // Create an ArrayList and add 3 elements. ArrayList list = new ArrayList(); list.Add("One"); // Add is your push list.Add("Two"); list.Add("Three"); } |
除了将值分配给该数组的特定索引外,我认为没有其他方法。
根据注释"那不是推送到数组。它只是分配给它"
如果您正在寻找将值分配给数组的最佳实践,那么这是分配值的唯一方法。
1 | Array[index]= value; |
只有当您不想使用
有两种方法可以完成此操作。
首先,转换为列表,然后再次转换为数组:
1 2 3 | List<int> tmpList = intArry.ToList(); tmpList.Add(anyInt); intArry = tmpList.ToArray(); |
现在不建议您这样做,因为您转换为列表然后再次返回数组。如果您不想使用列表,则可以使用第二种将值直接分配给数组的方法:
1 2 3 4 5 |
这是直接的方法,如果您不想纠缠列表和转化,
建议您使用。
我很抱歉使用CopyTo方法,所以这里是我的两个美分,一种易于解释和简单的解决方案。 CopyTo方法将数组复制到给定索引处的另一个数组。在这种情况下,将从索引1开始将myArray复制到newArr,以便newArr中的索引0可以容纳新值。
" Push "来排列在线演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
我不明白您在使用for循环在做什么。您仅在遍历每个元素并将其分配给遇到的第一个元素。如果您要推送到列表,请使用上面的回答,指出没有推送到列表的事情。这确实使数据结构混杂在一起。 Javascript可能不是最好的例子,因为JavaScript列表实际上同时也是队列和堆栈。
查看此文档页面:https://msdn.microsoft.com/zh-cn/library/ms132397(v = vs.110).aspx
"添加"功能是"方法"下的第一个功能。