C# 数组(5) 持续更新

同一类型和不同类型的多个对象

使用同一类型的多个对象,使用集合和数组。

使用不同类型的多个对象,使用Tuple(元组)。

初始化数组

int[] myArray = new int[4];

myArray 存放在栈中,而 myArray 的内容 放在 托管堆。

声明数组后,必须为数组分配内存,以保存数组的所有的元素。数组是引用类型,必须给它分配堆上的内存。

如果不知道数组包括多少元素,可以使用集合。

还可以用数组初始化器。

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

访问数组

myArray[0] = 12;
myArray[1] = 24;

如果使用错误的索引值,会抛出 IndexOutOfRangeException 错误。

遍历数组

int[] myArray = new int[4]{1,2,3,4};
for (int i = 0; i < myArray.Length; i++)
{
    Console.WriteLine("{0}",myArray[i]);
}
int[] myArray = new int[4]{1,2,3,4};
foreach (int val in myArray)
{
    Console.WriteLine("{0}",val);
}

foreach 语句利用了本章 IEnumerable 和 IEnumerator 接口。

使用引用类型

Person[] myPersons = new Person[2];
myPersons[0] = new Person() {FirstName = "FirstName1", LastName = "LastName1"};
myPersons[1] = new Person() { FirstName = "FirstName2", LastName = "LastName2" };

 

Person[] myPersons =
{
    new Person {FirstName = "FirstName1", LastName = "LastName1"},
    new Person {FirstName = "FirstName2", LastName = "LastName2"}
};

多维数组

int[,] mInts = new int[2,2];
mInts[0, 0] = 1;
mInts[0, 1] = 2;
mInts[1, 0] = 3;
mInts[1, 1] = 4;

 

int[,] mInts =
{
    {0, 1},
    {0, 2},
    {1, 1},
    {2, 2}
};

 

当然还可以声明三维数组

int[,,] mInts = new int[2,2,2];
mInts[0, 0, 0] = 0;
mInts[0, 0, 1] = 1;
mInts[0, 1, 0] = 2;
mInts[0, 1, 1] = 3;
mInts[1, 0, 0] = 4;
mInts[1, 0, 1] = 5;
mInts[1, 1, 0] = 6;
mInts[1, 1, 1] = 7;

 

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

锯齿数组

int[][] jagged = new int[3][];

在初始化锯齿数组是,只在第1对方括号中设置该数组包含的行数。定义各行中元素个数的第2个方括号设置为空,让数组每行包含不同元素个数,之后指定行中元素个数。

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

Array类

方括号声明数组, 实际上派生一个自抽象基类Array新类。foreach语句迭代数组,实际就是调用 Array中的 GetEnumerator()。

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

Array.Rank 获得数组的维数。

创建数组

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

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

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

创建多维数组

int[] lengths = {2, 3};
int[] lowerBounds = {1, 10};
Array racers = Array.CreateInstance(typeof(Person),lengths,lowerBounds);

复制数组

数组实现ICloneable接口,然后调用Clone返回浅副本。

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

copy必须传递足够元素的数组。

有时也需要深度克隆,但克隆的时候,要仔细思考一下,是否真的需要克隆。 

排序

int[] intArray1 = {99, 6, 3};
Array.Sort(intArray1);
foreach (var i in intArray1)
{
    Console.WriteLine(i);
}

如果数组是自定义类,那么类就要实现Icomparable接口

public class Person: IComparable<Person>
{

    public string FirstName { get; set; }
    public string LastName { get; set; }

    public int CompareTo(Person other)
    {
        if (other == null) return 1;
        int result = String.CompareOrdinal(this.LastName, other.LastName);
        if (result == 0)
        {
            result = String.CompareOrdinal(this.FirstName, other.FirstName);
        }
        return result;
    }

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

对象相等,方法返回0。小于 返回 小于0的值 ,大于返回 大于0的值。

如果不能修改类,还可以新类里 实现 IComparer 或 IComparer<T> 实现接口。

public enum PersonCompareType
{
    FirstName,
    LastName
}

public class PersonComparer : IComparer<Person>
{
    private PersonCompareType compareType;

    public PersonComparer(PersonCompareType compareType)
    {
        this.compareType = compareType;
    }


    #region IComparer<Person> Members

    public int Compare(Person x, Person y)
    {
        if (x == null) throw new ArgumentNullException("x");
        if (y == null) throw new ArgumentNullException("y");

        switch (compareType)
        {
            case PersonCompareType.FirstName:
                return String.Compare(x.FirstName, y.FirstName, StringComparison.Ordinal);
            case PersonCompareType.LastName:
                return String.Compare(x.LastName, y.LastName, StringComparison.Ordinal);
            default:
                throw new ArgumentException(
                      "unexpected compare type");
        }
    }

    #endregion
}
Array.Sort(persons, 
    new PersonComparer(PersonCompareType.FirstName));

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

数组协变

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

static void TestArray(object[] objects)

这里就可以传入 Person[] 参数进去了。

注意,数组协变只能用于引用类型,不能用于值类型。另外如果object[],被赋予了 Person[] 。就不能赋值其他类型的值了,否则运行时,会出现 ArrayTypeMismatchException 的异常。

ArraySegment<T>

ArraySegment表示数组的一部分。

static void Main(string[] args)
{
    int[] ar1 = {0, 1, 2, 3, 4, 5, 6};
    int[] ar2 = { 0, 1, 2, 3, 4, 5, 6 };

    ArraySegment<int>[] setmentInts = new ArraySegment<int>[2]
    {
        new ArraySegment<int>(ar1,0,3), 
        new ArraySegment<int>(ar2,3,3), 
    };

    int sum = SumOfSegments(setmentInts);
    Console.WriteLine(sum);
}

static int SumOfSegments(ArraySegment<int>[] segments)
{
    int sum = 0;
    foreach (var segment in segments)
    {
        //for (int i = segment.Offset; i < segment.Offset + segment.Count; i++)
        //{
        //    sum += segment.Array[i];
        //}

        foreach (var value in segment)
        {
            sum += value;
        }
    }
    return sum;
}

数组段不复制原数组的元素,但原数组可以通过ArraySegment<T>访问。如果数组段的元素改变了,这些变换就会反映到原数组中。

枚举

数组或集合实现带 GetEumerator方法的IEumerable 接口。 GetEumberator 方法返回一个实现Ieumerable 接口的枚举。然后 foreach语句就可以使用Ieumerable接口迭代集合。

IEnumerator接口

foreach使用IEnumerator接口的方法和属性,迭代集合中的所有方法。为此,IEnumerator定义了Current属性,来返回光标所在的元素,该接口的MoveNext()方法移动到集合的下一个元素上,如果有这个元素,该方法就返回true。如果集合不再有更多的元素,该方法返回false。

这个接口的泛型版本IEnumerator<T> 派生字接口 IDispose ,因此定义了 Dispose 方法,来清空枚举器占用的资源。

IEnumerator 接口还定义了 Reset() 方法,以与 COM 交互操作。

foreach语句

C#的 foreach 语句 不会解析为 IL代码中的 foreach语句。 C#编译器会把 foreach语句转换为 IEnumerator接口的方法和属性。

foreach (var value in ar1)
{
    Console.WriteLine(value);
}

解析为

int[] ar1 = {0, 1, 2, 3, 4, 5, 6};
int[] ar2 = { 0, 1, 2, 3, 4, 5, 6 };

IEnumerator enumerator = ar1.GetEnumerator();
while (enumerator.MoveNext())
{
    Console.WriteLine(enumerator.Current);
}

yield

yield可以创建枚举器,yield return语句返回集合一个元素,并移动到下一个元素上。yield break 可停止迭代。

public class SimpleConllection : IEnumerable<string>
{
    public IEnumerator<string> GetEnumerator()
    {
        yield return "Hello";
        yield return "World";
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

static void Main(string[] args)
{
    SimpleConllection simpleConllection = new SimpleConllection();
    foreach (string s in simpleConllection)
    {
        Console.WriteLine(s);
    }
}

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

使用迭代块,编译器会生成 yield 类型,其中包含一个状态机,代码如下

public class SimpleConllection
{
    public IEnumerator GetEnumerator()
    {
        return new Enumerator(0);
    }

    public class Enumerator : IEnumerator<string>, IEnumerator, IDisposable
    {
        private int state;
        private string current;

        public Enumerator(int state)
        {
            this.state = state;
        }

        bool System.Collections.IEnumerator.MoveNext()
        {
            switch (state)
            {
                case 0:
                    current = "Hello";
                    state = 1;
                    return true;
                case 1:
                    current = "World";
                    state = 2;
                    return true;
                case 2:
                    break;
            }
            return false;
        }

        public void Dispose()
        {
           
        }

        public void Reset()
        {
           
        }

        public string Current { get; private set; }

        object IEnumerator.Current
        {
            get { return current; }
        }
    }
}

yield 语句 会生成一个枚举器,而不仅仅生成一个包含的项的列表。这个枚举器通过 foreach 语句调用。foreach中访问每一项是,就会访问枚举器。这样迭代大数据时,无须一次把所有的数据读入内存。

元组 

数组存储相同类型的对象,元组存放不同类型的对象。.net 4.0 中 定义了 8个泛型Tuple类和一个静态Tuple类。用于创建Tuple。

http://blog.csdn.net/limlimlim/article/details/7835926

private static Tuple<int, int> Divide(int dividend, int divisor)
{
    return Tuple.Create<int, int>(dividend, divisor);
}

static void Main(string[] args)
{
    Tuple<int, int> result = Divide(13, 2);
    Console.WriteLine("result of divison:{0}," +
        "reminder:{1}", result.Item1, result.Item2);   
}

当然 元组 还可以套用 元组。

http://blog.csdn.net/limlimlim/article/details/7835926

 

数组和元组 都实现了 IStructuralComparable,IStructuralEquatable 接口。

IStructuralEquatable 用于比较两个元组或数组是否有相同的内容。

IStructuralEquatable 用于元组或数组排序。

 

Tuple 也提供了 Equals

http://www.tuicool.com/articles/iqMv2i

posted @ 2016-08-17 16:39  笨重的石头  阅读(264)  评论(0编辑  收藏  举报