zoukankan      html  css  js  c++  java
  • 《C#入门经典》学习笔记(集合、比较和转换)

    http://xiang-ai-2002.blog.163.com/blog/static/8477933201041824429161/


    集合


     

    C#中的数组是作为System.Array类的实例来执行的,它们是集合类中的一种
    集合类一般用于处理对象列表,其功能是通过执行System.Collection中的接口实现的

    集合的功能可以通过接口来实现
    该接口可以使用基本基本集合类,也可以创建自定义的集合类

    System.Collections 命名空间有很多接口提供了基本的集合功能:
    IEnumerable:公开枚举数,该枚举数支持在非泛型集合上进行简单迭代
    ICollection:定义所有非泛型集合的大小、枚举数和同步方法
    IList:表示可按照索引单独访问的对象的非泛型集合
    IDictionary:表示键/值对的非通用集合

    System.Array类继承了IList,ICollection和IEnumerable
    但不支持IList的一些高级功能,而且是一个大小固定的项目列表


    使用集合

    Systems.Collections中的一个类System.Collections.ArrayList,也执行IList,ICollection和IEnumerable接口,但与数组不同,它是大小可变的


    使用System.Array类的集合(数组),必须用固定的大小来初始化数组
    例如:
    Animal[] animalArray = new Animal[2];

    使用System.ArrayList类的集合,不需要初始化其大小
    例如:
    ArrayList animalArrayList = new ArrayList();
    这个类还有两个构造函数:
    1 把现有集合作为参数复制到新实例中
    2 用一个int参数设置集合的容量,不过实际内容超过容量时会自动增加


    初始化数组,需要给这个项目赋予初始化了的对象
    例如:
    Cow myCow1 = new Cow("Deirdre");
    animalArray[0] = myCow1;
    animalArray[1] = new Chicken("Ken");
    可以用这两种方式初始化数组

    对于ArrayList集合,需要用Add()方法添加新项目
    例如:
    Cow myCow2 = new Cow("Hayley");
    animalArrayList.Add(myCow2);
    animalArrayList.Add(new Chicken("Roy"));
    在添加万项目之后,就可以用与数组相同的语法重写他们
    例如:
    animalArrayList[1] = new Chicken("Roy2")


    Array数组和ArrayList集合都支持foreach结构来迭代
    例如:
    foreach (Animal myAnimal in animalArray)
    {
    }

    foreach (Animal myAnimal in animalArrayList)
    {
    }


    Array数组使用Length属性获取项目的个数
    例如:
    int animalCount = animalArray.Length;

    ArrayList集合使用Count属性获取项目的个数
    int animalCount2 = animalArrayList.Count;


    Array数组是强类型化的,可以直接使用数组的类型来存储项目
    即可以直接访问项目的属性和方法
    例如:
    对于类型是Animal的数组,Feed()是类Animal的方法
    animalArray[0].Feed();
    但对于类Animal派生类的方法,就不能直接调用,需要强制转换
    ((Chicken)animalArray[1]).LayEgg();

    ArrayList集合是System.Object对象的集合,通过多态性赋給Animal对象
    必须进行数据类型转换
    例如:
    ((Animal)animalArrayList[0]).Feed();
    ((Chicken)animalArrayList[1]).LayEgg();


    使用Remove()和RemoveAt()方法删除项目
    Remove  从 ArrayList 中移除特定对象的第一个匹配项(参数为特定对象)
    RemoveAt 移除 ArrayList 的指定索引处的元素(参数为索引值)
    删除项目后,会使其他项目在数组中移动一个位置

    使用AddRange()和InsertRange()方法可以一次添加多个项目
    AddRange 将 ICollection 的元素添加到 ArrayList 的末尾
    InsertRange 将集合中的某个元素插入 ArrayList 的指定索引处。 
    例如:
    animalArrayList.AddRange(animalArray);

    使用IndexOf()方法获取指定项目的索引值
    IndexOf 返回 ArrayList 或它的一部分中某个值的第一个匹配项的从零开始的索引。  
    可以通过索引值直接访问选项
    例如:
    int iIndex =  animalArrayList.IndexOf(myCow1);
    ((Animal)animalArrayList[iIndex]).Feed();


    定义集合

    可以从一个类派生自定义的集合
    推荐使用System.Collections.CollectionBase类
    CollectionBase类有接口IEnumerable,ICollection,和IList

    List属性可以通过Ilist接口访问项目,InnerList属性用于存储项目的ArrayList对象

    例如:
    public class Animals : CollectionBase
    {
        public void Add(Animal newAnimal)
        {
            List.Add(newAnimal);
        }

        public void Remove(Animal oldAnimal)
        {
            List.Remove(oldAnimal);
        }

        public Animals()
        {
        }
    }

    这个类用于生成Animal类型的集合,可以用foreach访问其成员:
    Animals animalCollection = new Animals();
    animalCollection.Add(new Cow("Sarah"));
    foreach (Animal myAnimal in animalCollection)
    {
    }
    如果要以索引的方式访问项目,就需要使用索引符


    索引符

    索引符是一种特殊类型的属性,可以把它添加到类中,提供类似数组的访问
    最常见的一个用法是对项目执行一个数字索引

    例如:
    在Animals集合中添加一个索引符
    public class Animals : CollectionBase
    {
        ...
        public Animal this[int animalIndex]
        {
            get
            {
                return (Animal)List[animalIndex];
            }
            set
            {
                List[animalIndex] = value;
            }
        }
    }

    this关键字与方括号一起,方括号中是索引参数
    对List使用一个索引符,而且显示声明了类型,因为IList接口返回的是System.Object对象
    现在可以用索引的方式访问项目:
    animalCollection[0].Feed();


    关键字值集合和IDictionary

    集合还可以执行类似的IDictionary接口,通过关键字值进行索引
    使用基类DictionaryBase,它也执行IEnumerable和ICollection接口,提供了对任何集合都相同的集合处理功能

    例如:
    public class Animals : DictionaryBase
    {
        public void Add(string newID, Animal newAnimal)
        {
            Dictionary.Add(newID, newAnimal);
        }

        public void Remove(string animalID)
        {
            Dictionary.Remove(animalID);
        }

        public Animals()
        {
        }

        public Animal this[string animalID]
        {
            get
            {
                return (Animal)Dictionary[animalID];
            }
            set
            {
                Dictionary[animalID] = value;
            }
        }
    }
    这样添加了Add()方法,Remove()方法和一个通过关键字访问项目的方法
    其中Dictionary是包含在DictionaryBase实例中的元素的列表

    DictionaryBase集合和CollectionBase集合在foreach的工作方式不同,
    DictionaryBase提供的是DictionaryEntry结构,需要通过Value成员获取对象本身
    例如:
    CollectionBase集合:
    foreach (Animal myAnimal in animalCollection)
    {
       myAnimal.Feed();
    }
    DictionaryBase集合:
    foreach (DictionaryEntry myEntry in animalCollection)
    {
       ((Animal)myEntry.Value).Feed();
    }


    迭代器

    通过IEnumerable接口,可以使用foreach循环获取对象
    foreach循环,迭代collectionObject的过程:
    1 调用Collection的GetEnumerator()方法返回一个IEnumerator引用
      该方法也可以通过IEnumerable接口的实现代码获得
    2 调用IEnumerator接口的MoveNext()方法,将枚举数推进到集合的下一个元素
    3 如果MoveNext()方法返回true,使用IEnumerator接口的Current属性获取对象的引用,用于foreach循环
    4 重复前两个步骤,直至MoveNext()返回false时,循环停止

    迭代器是一个按顺序提供要在foreach循环中使用的所有值的代码块
    一般这个代码块是一个方法,也可以使用属性访问器和其他代码块作为迭代器
    代码块的返回值可能是IEnumerable或IEnumerator接口类型:
    1 如果要迭代一个类,可使用方法IEnumerator(),其返回类型是IEnumerator
    2 如果要迭代一个类成员,则使用IEnumerable

    在迭代器块中,使用yield关键字选择要在foreach循环中使用的值
    语法:
    yield return value;
    例如:
    public static IEnumerable SimpleList()
    {
        yield return "string 1";
        yield return "string 2";
        yield return "string 3";
    }

    public static void Main(string[] args)
    {
        foreach (string item in SimpleList())
            Console.WriteLine(item);

        Console.ReadKey();
    }
    这里SimpleList就是迭代器块,是一个方法,使用IEnumerable返回类型
    可以从yield语句中返回任意类型

    可以中断信息返回foreach循环过程
    语法:yield break;

    迭代器和集合
    迭代器可以用于迭代储存在目录类型的集合中的对象
    例如:
    public new IEnumerator GetEnumerator()
    {
        foreach (object animal in Dictionary.Values)
            yield return (Animal)animal;
    }

    迭代集合中的对象:
    foreach (Animal myAnimal in animalCollection)
    {
    }


    深度复制

    使用System.Object.MemberwiseClone()方法可以进行阴影复制
    对于值类型成员,没什么问题
    但对于引用类型成员,新对象和源对象的成员将指向同一个引用对象
    例如:
    public class Content
    {
       public int Val;
    }
       
    public class Cloner
    {
       public Content MyContent = new Content();
       
       public Cloner(int newVal)
       {
          MyContent.Val = newVal;
       }

       public object GetCopy()
       {
          return MemberwiseClone();
       }
    }

    执行:
    Cloner mySource = new Cloner(5);
    Cloner myTarget = (Cloner)mySource.GetCopy();
    int iVal1 = myTarget.MyContent.Val;
    mySource.MyContent.Val = 2;
    int iVal2 = myTarget.MyContent.Val;

    结果:
    iVal1是5,iVal2是2

    但有时候需要的是分别引用各自的对象,使用深度复制就可以解决
    标准方式是添加一个ICloneable接口,该接口有一个Clone()方法
    该方法不带参数,返回一个对象类型
    例如:
    public class Content
    {
       public int Val;
    }
       
    public class Cloner : ICloneable
    {
       public Content MyContent = new Content();
       public int iVal = 0;
       
       public Cloner(int newVal)
       {
          MyContent.Val = newVal;
       }
       
       public object Clone()
       {
          Cloner clonedCloner = new Cloner(MyContent.Val);
          clonedCloner.iVal = iVal;
          return clonedCloner;
       }
    }

    通过Cloner对象的Val字段创建一个相同的Cloner对象
    如果有值成员需要复制,那必须给新对象添加上这个成员
    这样就能复制一个与源对象相同而互相独立的新对象

    用GetCopy换成Clone,执行相同的程序,结果:
    iVal1是5,iVal2是5

     

    比较



    类型比较

     

    比较对象时,需要先知道对象的类型,可以使用GetType()方法
    配合typeof()运算符一起使用,就可以确定对象的类型
    if (myObj.GetType() == typeof(MyComplexClass))
    {
       // myObj is an instance of the class MyComplexClass.
    }

    封箱和拆箱

    处理值类型时后台的操作:
    封箱(boxing)是把值类型转换为System.Object类型或由值类型实现的接口类型
    拆箱(unboxing)是相反的过程
    例如:
    结构类型:
    struct MyStruct
    {
        public int Val;
    }
    把类型结构放在object类型变量中封箱:
    MyStruct valType1 = new MyStruct();
    valType1.Val = 5;
    object refType = valType1;

    这里创建了一个MyStruct类型的valType1,给成员赋值后封箱到对象refType中
    这种方式的封装,将包含值类型的一个副本的引用,而不是源值的引用
    验证:
    valType1.Val = 6;
    MyStruct valType2 = (MyStruct)refType;
    结果valType2.Val是5而不是6

    如果对个引用类型进行封装,将包含源值的引用
    例如:
    class MyStruct
    {
       public int Val;
    }
    执行相同的操作,得到valType2.Val的值是6

    可以把值类型封箱到一个接口类型中:
    interface IMyInterface
    {
    }
       
    struct MyStruct : IMyInterface
    {
       public int Val;
    }

    把结构封箱到IMyInterface类型中:
    MyStruct valType1 = new MyStruct();
    IMyInterface refType = valType1;

    拆箱:
    MyStruct ValType2 = (MyStruct)refType;

    封箱是在没有用户干涉的情况下进行的
    拆箱一个值需要进行显式转换(封箱是隐式的转换)
    访问值类型的内容前,必须进行拆箱

    is运算符

    is运算符可以检查对象是否是给定的类型,或者是否可以转换为给定的类型
    语法:
    <operand> is <type>
    1 如果<type>是一个类类型,<operand>也是该类型,或继承了该类型,或封箱到该类型中,则返回true
    2 如果<type>是一个接口类型,<operand>也是该类型,或是实现该接口的类型,则返回true
    3 如果<type>是一个值类型,<operand>也是该类型,或封箱到该类型中,则返回true


    值比较

    运算符重载

    要重载运算符,可给类添加运算符类型成员(必须是static)
    例如:
    重载+运算符:

    Code

    运算符重载与标准静态方法声明类似,但它使用关键字operator和运算符本身
    使用:

    Code

    结果op3.val的值是9

    注意:如果混合了类型,操作数数序必须与运算符重载参数顺序相同

    可以重载的运算符:
    一元运算符:+,-,!,~,++,--,true,false
    二元运算符:+,-,*,/,%,&,|,^,<<,>>
    比较运算符:==,!=,<,>,<=,>=
    注意:
    如果重载true或false运算符,可以在布尔表达式中使用类,例如if (op1) {}

    运算符如 < 和 > 必须成对重载,但可以使用其他运算符来减少代码
    例如:

    Code

     同时适用于==和!=,常常需要重写Object.Equals()和Object.GetHashCode()

    Code


    IComparable和IComparer接口

    IComparable和IComparer接口是比较对象的标准方式
    区别:
    IComparable在要比较的对象的类中实现,可以比较该对象和另一个对象
    IComparer在一个单独的类中实现,可以比较任意两个对象

    .Net Framework 在类Comparer上提供了IComparer接口的默认实现方式,类Comparer位于System.Collections命名空间中,可以对简单类型以及支持IComparable接口的任意类型进行特定文化的比较

    public class SamplesComparer
    {
        public static void Main()
        {
            String str1 = "llegar";
            String str2 = "lugar";
            Console.WriteLine("Comparing "{0}" and "{1}" ", str1, str2);

            // Uses the DefaultInvariant Comparer.
            Console.WriteLine("   Invariant Comparer: {0}", Comparer.DefaultInvariant.Compare(str1, str2));

            // Uses the Comparer based on the culture "es-ES" (Spanish - Spain, international sort).
            Comparer myCompIntl = new Comparer(new CultureInfo("es-ES"false));
            Console.WriteLine("   International Sort: {0}", myCompIntl.Compare(str1, str2))
        }

    }


    一般使用IComparable给出类的默认比较代码,使用其他类给出非默认的比较代码
    IComparable提供一个CompareTo()方法比较两个对象,并返回一个int值
    例如:

        class Person : IComparable
        {
            public string Name;
            public int Age;

            public Person(string name, int age)
            {
                Name = name;
                Age = age;
            }


            public int CompareTo(object obj)
            {
                if (obj is Person)
                {
                    Person otherPerson = obj as Person;
                    return this.Age - otherPerson.Age;
                }

                else
                {
                    throw new ArgumentException( "Object to compare to is not a Person object.");
                }

            }

        }

    主程序代码

        class Program
        {
            static void Main(string[] args)
            {
                Person person1 = new Person("Jim"30);
                Person person2 = new Person("Bob"25);

                if (person1.CompareTo(person2) == 0)
                {
                    Console.WriteLine("Same age");
                }

                else if (person1.CompareTo(person2) > 0)
                {
                    Console.WriteLine("person 1 is Older");
                }

                else
                {
                    Console.WriteLine("person1 is Younger");
                }

            }

        }

    IComparer提供一个Compare()方法,接受两个对象返回一个整型结果
    例如:

        public class PersonComparer : IComparer
        {
            public static IComparer Default = new PersonComparer();

            public int Compare(object x, object y)
            {
                if (x is Person && y is Person)
                {
                    return Comparer.Default.Compare(((Person)x).Age, ((Person)y).Age);
                }

                else
                {
                    throw new ArgumentException(
                       "One or both objects to compare are not Person objects.");
                }

            }

        }

    主程序:

        class Program
        {
            static void Main(string[] args)
            {
                Person person1 = new Person("Jim"30);
                Person person2 = new Person("Bob"25);

                if (PersonComparer.Default.Compare(person1, person2) == 0)
                {
                    Console.WriteLine("Same age");
                }

                else if (PersonComparer.Default.Compare(person1, person2) > 0)
                {
                    Console.WriteLine("person 1 is Older");
                }

                else
                {
                    Console.WriteLine("person1 is Younger");
                }

            }

        }


    转换


     

    重载转换运算符

    implicit 关键字用于声明隐式的用户定义类型转换运算符
    explicit 关键字用于声明显式的用户定义类型转换运算符

    例如:
    public class ConvClass1
    {
        public int val;

        public static implicit operator ConvClass2(ConvClass1 op1)
        {
            ConvClass2 returnVal = new ConvClass2();
            returnVal.val = op1.val.ToString();
            return returnVal;
        }
    }

    public class ConvClass2
    {
        public string val;

        public static explicit operator ConvClass1(ConvClass2 op1)
        {
            ConvClass1 returnVal = new ConvClass1();
            returnVal.val = Convert.ToInt32(op1.val);
            return returnVal;
        }
    }

    使用:
    ConvClass1 op1 = new ConvClass1();
    op1.val = 5;
    ConvClass2 op2 = op1;
    这里使用了隐式转换,此时op2.val的值是字符"5"

    ConvClass2 op1 = new ConvClass2();
    op1.val = "6";
    ConvClass1 op2 = (ConvClass1)op1;
    这里使用了显示转换,此时op2.val的值是数字6


    as运算符

    as运算符可以把一种类型转换为指定的引用类型
    语法:
    <operand> as <type>

    只适用于:
    1 <operand>的类型是<type>类型
    2 <operand>可以隐式转换为<type>类型
    3 <operand>可以封箱到类型<type>中
    如果不能从<operand>转换为<type>,则表达式结果是null

    例如:
    class ClassA : IMyInterface
    {
    }
       
    class ClassD : ClassA
    {
    }

    ClassA obj1 = new ClassA();
    ClassD obj2 = obj1 as ClassD;

    obj2的结果是null

    使用一般的类型转换,出错时会抛出一个异常
    而as只会把null赋給对象,只要判断对象是否null就知道转换是否成功 

  • 相关阅读:
    UICollectionView添加 HeaderView FooterView
    Swift-UIDynamic初见
    Swift归档
    通知NSNotication&通知中心NSNoticationCenter
    iOS沙盒
    lldb-320.4.152﹣Debugger commands:
    UIPickerView採摘控件的學習
    hive的优化
    zookeeper简易配置及hadoop高可用安装
    hadoop组件概念理解
  • 原文地址:https://www.cnblogs.com/nafio/p/9137595.html
Copyright © 2011-2022 走看看