美好的一天
所以我知道这是一个相当广泛讨论的问题,但我似乎无法找到明确的答案.我知道你可以做我要求使用List的东西,但这不会解决我的问题.
我在初学者C#上教课.我有PHP和Java脚本背景,因此倾向于考虑这些语言.
我一直在寻找向学生展示如何动态添加元素的最佳方式.我们不能使用列表,因为这些不是我目前教授的教学大纲的一部分(尽管它们确实会在以后的版本中出现)本学期).
因此,我正在寻找执行Java array.push(newValue)类型场景的最简单方法,这种场景对新手编程人员来说是可以理解的,而不会教他们不好的做法.
到目前为止,我一直在解决这个问题:
for (int i = 0; i < myArray.Length; i++)
{
if(myArray[i] == null)
{
myArray[i] = newValue;
break;
}
}
Run Code Online (Sandbox Code Playgroud)
我只是需要知道这是否是一种可接受的方法,如果我教他们任何不良做法,或者是否有更好/更简单的方法来实现我的目标.
编辑问题的关键是元素需要添加到数组中的第一个空槽中,就像Java推送功能所做的那样.
任何建议将不胜感激.
jmc*_*ney 12
array.push就像List<T>.Add..NET数组是固定大小的,因此您无法实际添加新元素.您所能做的就是创建一个比原始元素大一个元素的新数组,然后设置最后一个元素,例如
Array.Resize(ref myArray, myArray.Length + 1);
myArray[myArray.GetUpperBound(0)] = newValue;
Run Code Online (Sandbox Code Playgroud)
编辑:
鉴于对此问题的编辑,我不确定这个答案是否真的适用:
问题的关键在于元素需要被添加到数组中的第一个空槽中,这就是Java推送功能所能做到的.
我提供的代码有效地附加了一个元素.如果目的是设置第一个空元素,那么你可以这样做:
int index = Array.IndexOf(myArray, null);
if (index != -1)
{
myArray[index] = newValue;
}
Run Code Online (Sandbox Code Playgroud)
编辑:
这是一个扩展方法,它封装了该逻辑并返回放置值的索引,如果没有空元素则返回-1.请注意,此方法也适用于值类型,将具有该类型的默认值的元素视为空.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
还有另一种方法可以做到这一点,它非常适合扩展类。
public static void Push<T>(ref T[] table, object value)
{
Array.Resize(ref table, table.Length + 1);
table.SetValue(value, table.Length - 1);
}
Run Code Online (Sandbox Code Playgroud)
这里发生的事情是,我们正在调整大小,然后为Array.Resize(...)方法创建的新元素设置值。
这是带有示例用法的片段。
如前所述,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;
Run Code Online (Sandbox Code Playgroud)
关于循环的想法:
当数组初始化时,数组的元素被设置为其默认值。因此,如果您想在保存引用类型(具有默认值null)的数组中填充“空白”,那么您的方法会很好。
但它不适用于值类型,因为它们是用0!
你的问题有点离题。特别是,您说“需要将元素添加到数组中的第一个空槽中,说谎(原文如此) Java 推送函数可以做到。”
动词“推送”在我所知道的除 JavaScript 之外的任何语言中都不能与数组一起使用。我怀疑它只存在于 JavaScript 中,因为它可能存在(因为 JavaScript 是一种完全动态的语言)。我很确定它不是故意设计的。
C# 中 JavaScript 风格的 Push 操作可以用这种有点低效的方式编写:
int [] myArray = new int [] {1, 2, 3, 4};
var tempList = myArray.ToList();
tempList.Add(5);
myArray = tempList.ToArray(); //equiv: myArray.Push(5);
Run Code Online (Sandbox Code Playgroud)
“推送”用于某些类型的容器,特别是堆栈、队列和双端队列(它们有两次推送 - 一次从前面,一次从后面)。我敦促您不要在对数组的解释中将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!
Run Code Online (Sandbox Code Playgroud)
该“功能”是 .NET Framework 初始版本中缺乏泛型类型和显式协变/逆变以及复制 Java“功能”的冲动的结果。
数组确实出现在许多核心 .NET API 中(例如 System.Reflection)。它们再次出现,因为初始版本不支持泛型集合。
在一般情况下,一个有经验的C#程序员也不会在他的应用程序中使用的许多阵列,宁愿使用更强大的集合,例如List<T>,Dictionary<TKey, TValue>,HashSet<T>和朋友。特别是,该程序员倾向于使用IEnumerable<T>所有集合都实现的接口来传递集合。IEnumerable<T>用作参数和返回类型(在可能且符合逻辑的情况下)的最大优点是通过IEnumerable<T>引用访问的集合是不可变的。这有点像const在 C++ 中正确使用。
在每个人都掌握了基础知识之后,您可能会考虑添加到有关数组的讲座中的一件事是新Span<T>类型。Span 可能会使 C# 数组变得有用。
最后,LINQ(语言集成查询)推出了很多的功能集合(通过添加扩展方法来IEnumerable<T>)。确保你的学生没有using System.Linq;在他们的代码顶部有一个语句 - 将 LINQ 混合到一个关于数组的初学者课程中会让他或她感到困惑。
BTW:你教的是什么课?在什么水平?