C#学习笔记Day7

第六章 数组

1.简单数组

         如果需要使用同一类型的多个对象,就可以使用数组。数组是一种数据结构,他可以包含同一类型的多个元素。

2.数组的声明

         在声明数组时,应该先定义数组中的元素类型,其后是一对中括号和变量名,例如:

Int[] myArray

3.初始化

         声明了数组,就必须为数组分配内存,以保存数组的所有元素。数组是引用类型,所以必须给他分配堆上的内存。为此,应使用new运算符,指定数组中的冤死的类型和数量来初始化数组变量。        

myArray=new int[4];

         注:指定了数组的大小之后,如果不复制数组中的所有元素,就不能重新设置数组的大小。如果事先不知道数组中应该包含多少元素,就可以使用集合。

        还可以使用数组初始化器为数组的每个元素赋值。数组初始化器只能在声明时使用,不能在声明数组之后使用。        

 Int[] myArray =new int[4] {1,2,3,4};

        如果使用花括号,还可以不指定数组的大小,因为编译器会自动统计元素的个数:      

 Int[] myArray =new int[] {1,2,3,4};

    使用c#编译器还有一种更简化的形式:

Int[] myArray = {1,2,3,4};

4.访问数组元素

         使用索引器访问

         使用for语句迭代数组中的所有元素

         使用foreach语句

5.使用引用类型

         除了能声明预定义类型的数组,还可以声明自定义类型的数组。下面用Person类来说明,这个类有两个自动实现的属性,FirstName和LastName,以及从Object类重写的ToString方法:        

 public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public override string ToString()
        {
            return String.Format("{0} {1}",FirstName,LastName);
        }
}

        声明一个包含两个Person元素的数组:Person[] myPersons=newPerson[2],但是必须注意,如果数组中的元素是引用类型,就必须为每个数组元素分配内存,若使用了数组中未分配内存的元素,则会报空引用类型的异常。

6.Array类

        Array类是抽象类,所以不能使用构造函数创建数组,但可以使用静态方法CreateInstance()来创建数组。如果事先不知道元素的类型,这个静态方法就非常有用,因为类型可以做贼Type对象传递给CreateInstance()方法。 

 Array intArray = Array.CreateInstance(typeof(int),5);

       这个例子说明了如何创建一个大小为5的int型数组。可以使用SetValue()方法设置对应元素的值,使用GetValue()方法读取对应元素的值。

7.复制数组

      因为数组是引用类型,所有经一个数组变量赋值给另一个数组,就会得到两个引用同一数组的变量。

8.枚举

       在foreach语句中使用枚举,可以迭代集合中的元素,且无需知道集合中的元素个数。数组或集合实现带GetEumerator()方法的接口,GetEumerator()方法返回一个IEnumerator类型对象。

public interface IEnumerable
{
    IEnumerator GetEnumerator();
}

public interface IEnumerator
{
    bool MoveNext();
    object Current { get; }
    void Reset();
}

Q:为什么在foreach中不能修改item的值?

A:因为current 是只读的。

9.yield语句

        对于yield关键字,首先看一下mdn的解释:

        如果你在语句中使用 yield 关键字,则意味着它在其中出现的方法、运算符或 get 访问器是迭代器。 通过使用 yield 定义迭代器,可在实现自定义集合类型的 IEnumerable 和 IEnumerator 模式时无需其他显式类。(不需要  IEnumerable<int> list = new List<int>()  这样去new

举个例子:
       

 static void Main(string[] args)
  {
 
     foreach (int i in Power(2, 8, ""))
     {
         Console.Write("{0} ", i);
     }
     Console.ReadKey();
  }

 public static IEnumerable<int> Power(int number, int exponent, string s)

 {
     int result = 1;

     for (int i = 0; i < exponent; i++)
     {
         result = result * number;
         yield return result;
     }
     yield return 3;
     yield return 4;
     yield return 5;
}

       首先看一下Power方法。该静态方法返回一个IEnumerablel<int>类型的参数。按照我们平常的做法。应该对数据执行一定操作,然后return一个IEnumerablel<int>类型的参数。我们把Power方法改造如下:

public static IEnumerable<int> Power(int number, int exponent, string s)
{
    int result = 1;
    //接口不能实例化,我们这儿new一个实现了IEnumerable接口的List
    IEnumerable<int> example = new List<int>();
    for (int i = 0; i < exponent; i++)
    {
        result = result * number;
        (example as List<int>).Add(result);
    }
    return example;
}

 

        这是我们平常的思路。但是这样做就有个问题。这儿要new一个List,或者任何实现了IEnumerable接口的类型。这样也太麻烦了吧。要知道IEnumerable是一个常用的返回类型。每次使用都要new一个LIst,或者其他实现了该接口的类型。与其使用其他类型,不如我们自己定制一个实现了IEnumerable接口专门用来返回IEnumerable类型的类型。我们自己定制也很麻烦。所以微软帮我们定制好了。这个类是什么,那就是yield关键字这个语法糖。

Yield中的特殊情况

但是我们在Power方法中写一些方法,编译器会如何处理?

 static void Main(string[] args)
{
    //这儿调用了方法。
    var test = Power(2, 8, "");
    Console.WriteLine("Begin to iterate the collection.");
    //Display powers of 2 up to the exponent of 8:
    foreach (int i in Power(2, 8, ""))
    {
        Console.Write("{0} ", i);
    }
    Console.ReadKey();
}
public static IEnumerable<int> Power(int number, int exponent, string s)
{
    int result = 1;
    if (string.IsNullOrEmpty(s))
    {
         //throw new Exception("这是一个异常");
        Console.WriteLine("Begin to invoke GetItems() method");
    }
    for (int i = 0; i < exponent; i++)
    {
        result = result * number;
        yield return result;
    }
    yield return 3;
    yield return 4;
    yield return 5;
}

        按照我们的理解当我们 var test = Power(2, 8, "");的时候确实调用了Power方法。此时应该程序打印Console.WriteLine("Begin to invoke GetItems() method");然后继续执行 Console.WriteLine("Begin to iterate the collection.");方法。所以打印顺序应该是:

      Begin to invoke GetItems() method

      Begin to iterate the collection.

      但是我们运行的时候却发现

https://images0.cnblogs.com/blog2015/497507/201504/031205287954706.png

        我们的打印方法并没有出现在Power方法中,而是被封装进了实现枚举接口的类方法  private bool MoveNext()中。所以方法不会立即被执行,而是在我们使用数据的时候被执行。如果对此机制不了解,就容易出现另外一些意想不到的问题。例如在Power方法中添加一些验证程序,如果不符合条件就抛出一个异常。这样的异常检查不会被执行。只有我们使用数据的时候才会执行。这样就失去了检查数据的意义。

猜你喜欢

转载自blog.csdn.net/weixin_39504659/article/details/84821520