Good day all
So I know that this is a fairly widely discussed issue, but I can't seem to find a definitive answer. I know that you can do what I am asking for using a List, but that won't solve my issue.
I teach a class in beginner C#. I have a PHP and Java Script background, and thus tend to think in terms of these languages.
I have been looking for the best way to show my students how to add elements to an array on the fly.We can't use Lists, as these are not part of the syllabus that I currently teach (although they do come in later in the semester).
I am thus looking for the simplest way to perform a Java array.push(newValue) type scenario that will be understandable to novice coders without teaching them bad practice.
Here is how I have been approaching the problem so far:
for (int i = 0; i < myArray.Length; i++)
{
if(myArray[i] == null)
{
myArray[i] = newValue;
break;
}
}
I just need to know if this is an acceptable approach, if I am teaching them any bad practices, or if there is a better / simpler way of achieving my goal.
EDIT 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.
Any advice would be greatly appreciated.
答案 0 :(得分:7)
array.push
is like List<T>.Add
. .NET arrays are fixed-size so you can't actually add a new element. All you can do is create a new array that is one element larger than the original and then set that last element, e.g.
Array.Resize(ref myArray, myArray.Length + 1);
myArray[myArray.GetUpperBound(0)] = newValue;
EDIT:
I'm not sure that this answer actually applies given this edit to the question:
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.
The code I provided effectively appends an element. If the aim is to set the first empty element then you could do this:
int index = Array.IndexOf(myArray, null);
if (index != -1)
{
myArray[index] = newValue;
}
EDIT:
Here's an extension method that encapsulates that logic and returns the index at which the value was placed, or -1 if there was no empty element. Note that this method will work for value types too, treating an element with the default value for that type as empty.
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;
}
}
答案 1 :(得分:2)
This is acceptable as assigning to an array. But if you are asking for pushing, I am pretty sure its not possible in array. Rather it can be achieved by using Stack, Queue or any other data structure. Real arrays doesn't have such functions. But derived classes such as ArrayList have it.
答案 2 :(得分:2)
如前所述,List
提供了以干净的方式添加元素的功能,要对数组执行相同的操作,则必须调整它们的大小以容纳额外的元素,请参见下面的代码:
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;
关于循环的想法:
初始化数组时, 数组的元素将设置为其默认值。因此,如果您想在包含引用类型(其默认值为null
)的数组中填充“空白”,您的方法将是很好的。
但是它不适用于值类型,因为它们是用0
初始化的!
答案 3 :(得分:2)
这是我的解决方案
public void ArrayPush<T>(ref T[] table, object value)
{
Array.Resize(ref table, table.Length + 1); // Resizing the array for the cloned length (+-) (+1)
table.SetValue(value, table.Length - 1); // Setting the value for the new element
}
如何使用?就这么简单。
Array Push Example
string[] table = { "apple", "orange" };
ArrayPush(ref table, "banana");
Array.ForEach(table, (element) => Console.WriteLine(element));
// "apple"
// "orange"
// "banana"
非常简单实用。
这有点棘手,但它有效
答案 4 :(得分:1)
除了将值分配给该数组的特定索引外,我认为没有其他方法。
答案 5 :(得分:1)
C#在这方面与JavaScript有点不同。由于进行了严格的检查,因此 you 定义了数组的大小,并且 you 应该了解有关数组的所有信息,例如边界,最后一项放置在何处以及什么内容项目未使用。如果要调整其大小,应该将数组的所有元素复制到一个更大的新元素中。
因此,如果您使用原始数组,则除了保持最后一个空的可用index
索引外,其他方法别无选择,就像您已经在做的一样。
但是,如果您希望运行时维护此信息并完全抽象出数组,但仍在下面使用数组,则C#提供了一个名为ArrayList
的类来提供此抽象。
ArrayList
抽象了Object
的松散类型数组。参见来源here。
它处理所有问题,例如调整数组大小,维护最后可用的索引等。但是,它从您那里抽象/封装了此信息。您只使用ArrayList
。
要将任何类型的项推入基础数组末尾的基础数组中,请像这样:在ArrayList
上调用Add
方法:
/* you may or may not define a size using a constructor overload */
var arrayList = new ArrayList();
arrayList.Add("Foo");
编辑:有关类型限制的说明
就像每种编程语言和运行时一样,C#将堆对象与那些不会出现在堆上并且只会保留在函数的参数堆栈中的对象进行分类。 C#通过名称值类型与引用类型指出了这一区别。其值在堆栈上的所有事物都称为值类型,而将在堆上的所有事物称为引用类型。这大致类似于JavaScript对 objects 和 literals 的区分。
无论事物是值类型还是引用类型,都可以将任何内容放入C#中的ArrayList
中。就 typelessness 而言,这使它最接近JavaScript数组,尽管三个(JavaScript数组,JavaScript语言和C#ArrayList)实际上都不属于类型。
因此,您可以将所需的所有内容放入数字{,{1}}中,然后输入数字文字,字符串文字,组成的类的对象,布尔值,浮点数,双精度数,结构。
这是因为ArrayList
在内部维护并存储您放入其中的所有内容,并将它们存储在ArrayList
的数组中,就像您在我的原始答案和链接的源代码中所指出的那样。 / p>
当您放置非对象的东西时,C#创建一个类型为Object
的新对象,将您放入Object
中的东西的值存储到这个新的{{1}中}类型对象。此过程称为装箱,与JavaScript装箱机制没有什么不同。
例如在JavaScript中,虽然您可以使用数字文字来调用ArrayList
对象上的函数,但不能在数字文字的 prototype 中添加任何内容。
Object
就像JavaScript在对Number
方法的调用中将数字文字4装箱一样,C#在将非对象的所有事物放入// Valid javascript
var s = 4.toString();
// Invalid JavaScript code
4.prototype.square = () => 4 * 4;
var square = 4.square();
时也将它们包装成toString
类型。
Object
这涉及一定的惩罚,就像JavaScript一样。
在C#中,您可以避免这种损失,因为C#提供了ArrayList
的强类型版本,称为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`.
。因此,您不能将任何放入ArrayList
中;只是List<T>
类型。
但是,我从您的问题文本中假设您已经知道C#具有强类型项目的通用结构。您的问题是要具有像JavaScript这样的数据结构,表现出 typelessness 和弹性的语义,就像JavaScript List<T>
对象一样。在这种情况下,T
最接近。
从您的问题中也很明显,您的兴趣是学术上的,而不是在生产应用程序中使用该结构。
因此,我假设对于生产应用程序,您已经知道通用/强类型数据结构(例如Array
)的性能要优于非类型数据结构(ArrayList
)。例如)。
答案 6 :(得分:1)
您的问题有点离题了。特别是,您说“需要将元素添加到数组的第一个空插槽中,即Java推函数可以使用的(sic)。”
动词“ Push”不是我所知道的任何语言都用于Array的东西,除了JavaScript。我怀疑它只是在JavaScript中,因为它可能存在(因为JavaScript是一种完全动态的语言)。我很确定它不是故意设计的。
可以用这种效率低下的方式编写C#中JavaScript样式的Push操作:
int [] myArray = new int [] {1, 2, 3, 4};
myArray = myArray.ToList().Add(5).ToArray(); //equiv: myArray.Push(5);
“推送”用于某些类型的容器,尤其是Stacks,Queues和Deques(获得两次推送-一次从前面,一次从后面)。我敦促您在对数组的解释中不要将 Push 用作动词。它不会增加CS学生的词汇量。
在C#中,就像在大多数传统过程语言中一样,数组是包含在固定长度的连续内存块中的单一类型元素的集合。分配数组时,将分配每个数组元素的空间(在C#中,这些元素被初始化为类型的默认值,对于引用类型,为 null )。
在C#中,引用类型的数组用对象引用填充;值类型的数组填充有该值类型的实例。结果,由4个字符串组成的数组使用的内存与由应用程序类的4个实例组成的数组使用的内存相同(因为它们都是引用类型)。但是,由4个DateTime实例组成的数组比由4个短整数组成的数组要长得多。
在C#中,数组的实例是System.Array(引用类型)的实例。数组具有一些属性和方法(例如 Length 属性)。否则,数组就无济于事:您可以使用数组 index 从(或向)单个元素读取(或写入)。 T类型的数组也实现IEnumerable<T>
,因此您可以遍历数组的元素。
数组是可变的(可以写入数组中的值),但是它们具有固定的长度-不能扩展或缩短。它们是有序的,并且不能重新排列(除非通过手动调整值)。
C#数组是协变的。如果您要问C#语言设计师,这将是他们最后悔的功能。这是打破C#类型安全性的几种方法之一。考虑以下代码(假设Cat和Dog类从Animal继承):
Cat[] myCats = new Cat[]{myCat, yourCat, theirCat};
Animal[] animals = (Animal[]) myCats; //legal but dangerous
animals[1] = new Dog(); //heading off the cliff
myCats[1].Speak(); //Woof!
“功能”是由于.NET Framework的初始版本中缺少通用类型和显式协方差/协方差以及复制Java“功能”的冲动的结果。
数组确实出现在许多核心.NET API(例如System.Reflection)中。它们再次出现在这里,因为初始版本不支持通用集合。
通常,有经验的C#程序员不会在他的应用程序中使用很多数组,而是倾向于使用功能更强大的集合,例如List<T>
,Dictionary<TKey, TValue>
,HashSet<T>
和朋友。特别是,该程序员倾向于使用IEnumerable<T>
传递所有集合实现的接口来传递集合。使用IEnumerable<T>
作为参数和返回类型(在可能和逻辑上)的最大优点是,通过IEnumerable<T>
引用访问的集合是不可变的。有点像在C ++中正确使用const
。
在每个人都掌握了基础知识之后,您可能会考虑在数组的讲课中增加一种新的Span<T>
类型。跨度可能会使C#数组有用。
最后,LINQ(语言集成查询)为集合引入了许多功能(通过向IEnumerable<T>
添加扩展方法)。确保您的学生在他们的代码顶部没有using System.Linq;
语句-将LINQ混入数组中的新进班级会使他或她感到困惑。
顺便说一句:您要教哪种班?在什么级别?
答案 7 :(得分:0)
As per comment "That is not pushing to an array. It is merely assigning to it"
If you looking for the best practice to assign value to array then its only way that you can assign value.
Array[index]= value;
there is only way to assign value when you do not want to use List
.
答案 8 :(得分:0)
The is no array.push(newValue)
in C#. You don't push to an Array in C#. What we use for this a List
. What you may want to consider (for teaching purpose only) is the ArrayList
(no generic and it is a IList, so ...).
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");
}
答案 9 :(得分:0)
有两种方法可以完成此操作。
首先,是转换为列表,然后再次转换为数组:
List<int> tmpList = intArry.ToList();
tmpList.Add(anyInt);
intArry = tmpList.ToArray();
现在不建议您这样做,因为您转换为列表然后再次返回数组。如果您不想使用列表,则可以使用第二种方法,即直接将值分配给数组:
int[] terms = new int[400];
for (int runs = 0; runs < 400; runs++)
{
terms[runs] = value;
}
这是直接的方法,如果您不想纠缠列表和转化, 建议您这样做。
答案 10 :(得分:-2)
我很抱歉使用https://github.com/elementor/elementor/issues/7445#issuecomment-472822406,因此,这是我的两个美分,一种易于解释和简单的解决方案。 CopyTo方法将数组复制到给定索引处的另一个数组。在这种情况下,将从索引1开始将myArray复制到newArr,以便newArr中的索引0可以保存新值。
var newValue = 1;
var myArray = new int[5] { 2, 3, 4, 5, 6 };
var newArr = new int[myArray.Length + 1];
myArray.CopyTo(newArr, 1);
newArr[0] = newValue;
//debug
for(var i = 0; i < newArr.Length; i++){
Console.WriteLine(newArr[i].ToString());
}
//output
1
2
3
4
5
6
答案 11 :(得分:-3)
查看此文档页面:https://msdn.microsoft.com/en-us/library/ms132397(v=vs.110).aspx
“添加”功能是“方法”下的第一个功能。
答案 12 :(得分:-3)
我不明白您在使用for循环做什么。您仅在遍历每个元素并将其分配给遇到的第一个元素。如果您要推送到列表,请使用上面的回答,指出没有推送到列表的事情。这确实使数据结构混杂在一起。 Javascript可能不是最好的例子,因为JavaScript列表实际上同时也是队列和堆栈。