C#高编

1.简单数组:

如果需要使用同一类型的多个对象,就可以使用数组。数组是引用类型,所以必须给它分配堆上的内存,为此,应使用new运算符。

数组的声明方式:

int[] myArray;
myArray = new int[4];
或
int[] myArray = new int[4];
或
int[] myArray = new int[4]{4,7,11,2};//初始化器只能在声明变量时使用,不能再声明数组之后使用
int[] myArray = new int[]{4,7,11,2};//编译器会自动统计元素个数
int[] myArray = {4,7,11,2};

声明自定义类型的数组:

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[] myPersons = new Person[2];

 注意:如果数组中的元素是引用类型,就必须为每个数组元素分配内存。

myPerson[0] = new Person{FirstName = "Ayrton",LastName = "Senna"};

 也可以对自定义类型使用数组初始化器:

Person[] myPerson2 = 
{
    new Person { FirstName = "Ayrton", LastName = "Senna"},
    new Person { FirstName = "Michael",LastName = "Schumacher"}
};

 2.多维数组

声明二维数组,需在方括号中加上一个逗号。数组在初始化应指定每一维的大小(也成为阶)。声明数组之后,就不能修改其阶数了。

nt[,] twodim = new int[3,3];
twodim[0,0] = 1;twodim[0,1] = 2;twodim[0,2] = 3;
twodim[1,0] = 4;twodim[1,1] = 5;twodim[1,2] = 6;
twodim[2,0] = 7;twodim[2,1] = 8;twodim[2,2] = 9;

 如果事先指定元素的值,也可以使用数组索引器来初始化二维数组。

int[,] twodim = {  {1,2,3},
                   {4,5,6},
                   {7,8,9}
                };

 声明三维数组

int[,,] threedim = {
{ { 1, 2 }, { 3, 4 } },
{ { 5, 6 }, { 7, 8 } },
{ { 9, 10 }, { 11, 12 } }
};

 3.锯齿数组

锯齿数组相比二维数组,大小设置比较灵活,每一行都可以有不同的大小。

在声明锯齿数组时,要依次放置左右括号。在初始化时,在第一对方括号中设置该数据包含的行数。定义各行中元素个数的第2个方括号设置为空。

int[][] jagged = new int[3][];
jagged[0] = new int[2] { 1,2 };
jagged[1] = new int[6] { 3,4,5,6,7,8 };
jagged[2] = new int[3] { 9,10,11 };

4.Array类

LongLength属性:如果数组包含的元素个数超出了整数的取值范围,就可以使用其来获得元素的个数。

Rank属性:可以获得数组的维数。

Array类是一个抽象类,所以不能使用构造函数来创建数组。但除了可以使用C#语法创建数组实例之外,还可以使用静态方法CreateInstance()创建数组。如果事先不知道元素的类型,则该静态方法就非常有用。

可以用SetValue()方法设置对应元素的值,用GetValue()方法读取对应元素的值。

Array intArray1 = Array.CreateInstance(typeof(int),5);
for(int i = 0; i < 5, i++)
{
    intArray1.SetValue(33,i);
}

for(int i = 0; i < 5, i++)
{
    Console.WriteLine(intArray1.GetValue(i);
}

还可以将已创建的数组强制转换成声明为int[]的数组:

int[] intArray2 = (int[])intArray1;

CreateInstance()的重载:创建多维数组和不基于0的数组。

int[] lengths = { 2,3 };//设置长度,2*3个元素的二维数组
int[] lowerBounds = { 1,10 };//第一维基于1,第二维基于10
Array racers = Array.CreateInstance(typeof(Person), lengths, lowerBounds);
//SerValue()方法设置数组的元素,其参数是每一维的索引:
racers.SetValue(new Person
{
    FirstName = "Alain",
    LastName = "Prost"
}, 1, 10);
racers.SetValue(new Person
{
    FirstName = "Emerson",
    LastName = "Fittipaldi"
}, 1, 11);
......

尽管数组不是基于0,但可以用一般的C#表示法将它赋予一个变量。只需注意不要超出边界即可。

Person[,] racer2 = (Person[,])racers;
Person first = racers2[1,10];
Person last = racers2[2,12];

5.复制数组:

使用Clone()方法,实现ICloneable接口

int[] intArray1 = { 1,2 };
int[] intArray2 = (int[])intArray1.Clone();

如果数组的元素是值类型,则会复制所有值。

如果数组包含引用类型,则不复制元素,而只复制引用。

使用Array.Copy()方法,创建浅表副本。

二者区别:Clone方法会创建一个新数组,而Copy方法必须传递阶数相同且有足够元素的已有数组。

4.2排序:

使用Array.Sort()方法,但需要数组中的元素实现IComparable接口。简单类型(如String和Int32)已实现IComparable接口,所以可以对包含这些类型的元素进行排序。

string[] names = { "Shakira","Beyonce" };
Array.Sort(names);

自定义类实现排序:

实现IComparable接口的CompareTo()方法,如果比较的对象相等,则该方法返回0,如果该实例应排在参数对象的前面,则返回小于0的值。反之返回大于0的值。

public class Person:IComparable<Person>
{
    public int CompareTo(Person other)
    {
        if(other == null) throw new ArgumentNullException("other");
        
        int result = this.LastName.CompareTo(other.LastName);//因为lastName属性是string简单类型,因此具有CompareTo()方法

        if(result == 0)
        {
            result = this.FirstName.CompareTo(other.FirstName);
        }
        return result;
    }
    //...
}

 如果Person对象的排序方式与上述不同,或者不使用为数组中的元素,则可以实现IComparer接口或IComparer<T>接口,定义方法Compare(),它独立于要比较的类,所以需传入两个比较的参数。

 6.数组协变

数组支持协变,这表示数组可以声明为基类,其派生类型的元素可以赋予数组元素。

数组协变只能用于引用类型,不能用于值类型。

7.ArraySegment<T>

结构ArraySegment<T>表示数组的一段。如果某方法应返回数组的一部分,或者给某方法传递数组的一部分,就可以使用数组段。

static int SumOfSegments(ArraySegment<int>[] segments)
{
...
}

 8.枚举

  • 在foreach中使用枚举,可以迭代集合中的元素,且无需知道集合中的元素个数。foreach语句使用了一个枚举器。
  • 数组或集合实现带GetEumerator()方法的IEumerable接口。方法返回一个实现接口的枚举。接着,foreach语句就可以使用接口迭代集合了。
  • foreach语句并不一定需要在集合类中实现这个接口,有一个名为GetEnumerator()的方法,它返回实现了IEnumerator接口的对象就足够了。

foreach语句:首先,调用GetEnumerator()方法,获得数组的一个枚举器。在while循环中—只要MoveNext()返回true—就用Current属性访问数组中的元素:

foreach(var p in persons)
{
    Console.WriteLine(p);
}

以上语句解析为下面的代码段

IEnumerator<Person> enumerator = persons.GetEnumerator();
while(enumetor.MoveNext())
{
    Person p = enumerator.Current;
    Colsole.Writeline(p);
}

yield语句:返回集合的一个元素,并移动到下一个元素上。yield break可停止迭代。

包含yield语句的方法或属性也称为迭代块。迭代块可以声明为返回IEnumerator或IEnumerable接口,或者这些接口的泛型版本。可以包含多条yield return语句或yield break语句,但不能包含return语句。

注:在C#1.0中,使用foreach可以轻松迭代集合,但创建枚举器仍需要做大量工作,C#2.0添加了yield语句,以便于创建枚举器。

public class HelloCollection
{
    public IEnumerator<string> GetEnumerator()
    {
        yield return "Hello";
        yield return "World";
    }
}
public class MusicTitles
{
    string[] names = { "Tubular Bells","Hergest Ridge",
"Ommadawn","Platinum" };
    
    public IEnumerator<string> GetEnumerator()
    {
        for(int i = 0;i<4;i++)
        {
            yield return names[i];
        }
    }

    public IEnumerator<string> Reverse()
    {
        for(int i = 3;i>=0;i--)
        {
            yield return names[i];
        }
    }

    public IEnumerable<string> Subset(int index,int length)
    {
        for(int i = 0;i<index + length;i++)
        {
            yield return names[i];
        }
    }
}
View Code

类MusicTiltles可以用默认方式迭代集合,用Reverse()方法逆序迭代标题,用Subset() 方法迭代子集。

迭代字符串数组的客户端代码先使用GetEnumerator()方法,但不必在代码中编写,因为是默认方法。其他方法需要指定。

var titles = new MusicTitles();
//正序
foreach( var title in titles)
{
    Console.WriteLine(title);
}

//逆序
foreach(var title in titles.Reverse())
{
    Console.WriteLine(title);
}

9.元组

数组合并了相同类型的对象,而元组合并了不同类型的对象。元组起源于函数编程语言(如F#),在这些语言中频繁使用元组。

.NET4定义了8个泛型Tuple类和一个静态Tuple类,它们用作元组的工厂。不同的Tuple类支持不同数量的元素。如Tuple<T1>包含一个元素,Tuple<T1,T2>包含两个元素。

10.结构比较

数组和元祖都实现接口IStructuralEquatable和IStructuralComparable。这两个接口是.NET4新增的,不仅可以比较引用,还可以比较内容。

这些接口都是显式实现的,所以在使用时需要把数组和元祖强制转换为这个接口。

接口一用于比较两个元祖或数组是否有相同的内容,接口二用于给元祖或数组排序。

原文地址:https://www.cnblogs.com/KevinG/p/3533209.html