zoukankan      html  css  js  c++  java
  • 类和结构(一)

      类和结构实际上都是创建对象(实例)的模版,每个对象都包含数据,并提供了处理和访问数据的方法。

      类定义了类的每个对象可以包含什么数据和功能。

    class PhoneCus
    
        {
    
            public const string DaySend = "Mon";
    
            public int CusId;
    
        }

      结构与类的区别是它们在内存中的存储方式,访问方式和它们的一些特性(稍后详细介绍它们的区别)。

      较小的数据类型使用结构可提高性能,在语法上,比较类似,主要区别是使用关键字struct代替class来声明结构。

        struct PhoneCusStruct
        {
            public const string DaySend = "Mon";
            public int CusId=;
        }    

      对于类和结构,都是用new来声明实例:这个关键字创建对象并对其进行初始化。

    PhoneCus myCus = new PhoneCus();
    PhoneCusStruct  myCus2 = new PhoneCusStruct();

      上面的例子,类和结构的字段值都默认0.

    一.类

      类中的数据和函数称为类的成员(数据成员和函数成员)。

    1.数据成员

      数据成员是包含类的数据————字段,常量和事件的成员。数据成员可以是静态数据。类成员总是实例成员,除非用static显示声明。

    2.函数成员

      函数成员提供了操作类中数据的某些功能,包括方法,属性,构造函数,终结器,运算符以及索引。

      (1)方法

      *C#区分函数和方法。C#中函数包含上述提到的。

      *给方法传递参数

      参数可以通过引用或值传递给方法。在变量通过引用传递给方法时,被调用的方法得到的就是这个变量,准确的説就是指向内存中变量的指针。所以在方法内对变量进行的任何改变在方法退出后仍然有效。

      而如果变量通过值传递给方法,被调用的方法得到的是变量的一个相同副本,也就是说,在方法退出后,对变量的修改会丢失。

      对于复杂的数据类型,按引用传递的效率更高,因为在按值传递时,必须复制大量的数据。

      注意字符串的行为方式有所不同,因为字符串是不可变的,所以字符串无法采用一般引用类型的行为方式。在方法调用中,对字符串所做的改变都不会影响原始字符串。

      *ref参数

      像上面所説,值类型通过值传递变量是默认的。但也可以迫使值参数通过引用传递给方法。为此要使用ref关键字。这样该方法对变量所做的任何改变都会影响原始值。

            static void SomeFunction(int[] ints,ref int i)
            {
                ints[0] = 100;
                i = 100;
            }        

      在调用该方法的时候,必须添加ref关键字。

      SomeFunction(ints, ref i);

      *out参数

      C#要求变量在被引用前必须用一个初始值进行初始化。但使用out关键字来初始化可以简化C# 编译器所坚持的输入参数的初始化。

      在方法的输入参数前加上out前缀时,传递给该方法的变量可以不初始化。而且该变量通过引用传递,所以在从被调用的方法中返回时,对应方法对该变量进行的任何改变都会保留下来。

      在调用该方法时,仍需要使用out关键字:

            static void SomeFunction(int[] ints,out int i)
             {
                    ints[0] = 100;
                     i = 100;
             }
    
            SomeFunction(ints, out i);       

      *命名参数

      参数一般需要按定义的顺序传递给方法。命名参数允许按任意顺序传递。

            string FullName(string firstName,string lastName)
        {
            renturn firstName+" " +lastName; 
        }    

      调用方法:

                FullName("John","Doe");
            FullName(lastName:"Doe",firstName:"John");

      *可选参数

      参数也可以是可选的。必须为可选参数提供默认值。可选参数还必须是方法定义的最后一个参数。

            void TestMethod(int notOption,int option = 10)
            {
                Console.WriteLine( notOption + option);
            }        

      *方法的重载

      C#支持方法的重载————方法的几个版本有不同的签名(方法名相同,但参数的个数和/或类型不同)。

            class MathTest
              {
                 public int Value;
                 public int GetSquare()
                 {
                        return Value*Value;
                 }
    
                 public  int GetSquare(int x)
                 {
                    return x*x;
                 }
              }

      重载方法在参数方面的一些限制:

      两个方法不能仅在返回类型上有区别;

      两个方法不能仅根据参数是声明为ref还是out来区分。

      在任何语言中,对于方法重载,如果调用了错误的重载方法,就有可能出现运行错误。(后面讨论如何避免这些错误)。

      (2)属性(property)

      属性是一个方法或一对方法,在客户端看来,它是一个字段。

            public string  SomeProperty
    
            {
                get
               {
                    return "value";
                }
                set
                {
                    //设置属性值
                }
    
            }

      get访问器不带任何参数,且必须返回属性声明的类型。也不应为set访问器指定任何显示参数,编译器会

    假定它带一个参数,器类型也许属性相同,并表示为value.

            private int age
            public int  Age
            {
                get
                {
                    return age;
                }
                set
                {
                    age = valeu;
                }
            }

      注意所用的命名约定,采用C#的区分大小写模式,使用相同的名称,但公有属性采用大写形式命名,如果存在一个等价的私有字段,则采用小写形式命名。

      一些开发人员喜欢使用把下划线作为前缀的字段名,如_age,这会为识别字段提供极大的便利。

      *只读和只写属性

      在属性定义中省略set访问器,就会创建只读属性。这样客户端代码只可以读取该属性的值,但不能设置值。

            private int age
            public int  Age
            {
                get
                {
                    return age;
                }
            }

      同样在属性定义中省略get访问器,就会创建只写属性。

      *属性的访问修饰符

      C#允许给属性的gei和set访问器设置不同的访问修饰符,所以属性可以有公有的get访问器和受保护的set访问器。

      在gey和set访问器中,必须有一个具有属性的访问级别(公有)。

      *自动实现的属性

      如果属性的set和get访问器中没有任何逻辑,就可以使用自动实现的属性。这种属性会自动实现后背成员变量。

            public int  Age
            {
                getset;
            }

      不需要声明private int age;,编译器会自动创建它。

      使用自动实现的属性,就不能在属性设置中验证属性的有效性。但必须有两个访问器,不能把属性设置为只读或只写。

      public int  Age

      {

        get;//报错

      }

      但是,每个访问器的访问级别可以不同,

      public int  Age

      {

        get;

        private set;

      }

      (3)构造函数

      声明基本构造函数就是声明一个与包含的类同名的方法,但该方法没有返回值。

      public class MyClass

      {

        public MyClass()

        {

        }

        //

      }

      一般情况下,如果没有提供任何构造函数,编译器会在后台创建一个默认的构造函数。这是一个基本的构造函数,它只能把所有的成员字段初始化为标准的默认值。这通常就足够了,否则需要编写自己的构造函数。

      构造函数的重载与其它方法的规则相同。可以为构造函数提供任意多的的重载,只要它们的签名有明显区别。

            public class MyClass
            {
                public MyClass()
                {
    
                }
    
                public MyClass(int i )
                {
                    / /
                }
                //
            }

      如果提供了带参数的构造函数,编译器就不会自动提供默认的构造函数。只有在没有定义任何构造函数的时候,编译器才会自动提供默认的构造函数。

            public class MyNum
            {
                private int number;
                public MyNum(int number)
                {
                    this.number  =number;
                }
            }

      一般使用this关键字区分成员字段和同名的参数。

      如果试图使用无参数的构造函数实例化对象就会报错:

      MyNum num = new MyNum();//报错

      可以把构造函数定义为private或protected,这样不相关的类就不能访问它们:

            public class MyNum
            {
                private int number;
                private MyNum(int number)
                {
                    this.number  =number;
                }
            }

      上述例子没有为MyNum定义为任何公有或受保护的构造函数。这就使MyNum不能使用new运算符在外部代码中实例化,但可以在MyNum类中编写一个公有静态属性或方法,以实例化该类。

      这在下面两种情况下受有用的:

      类仅用作某些静态成员或属性的容器,因此永远不会实例化它。

      希望类仅通过某个静态成员函数来实例化。

      *静态构造函数

      C#可以给类编写无参数的静态构造函数。这种构造函数只执行一次,而前面的构造函数是实例构造函数,只要创建类的对象,就会

    执行它。

      class MyClass

      {

        static MyClass()

        {

        }

      }

      编写静态构造函数的一个原因是,类有一些静态字段或属性,需要在第一次使用类之前,从外部源中初始化这些静态字段和属性。

      .NET运行库不能确保什么时候执行静态构造函数,所以不能把要求在某个特定时刻执行的代码放在静态构造函数中。也不能预计不同类的静态构造函数按照什么顺序执行。但是可以确保静态构造函数最多运行一次,就在代码引用类之前调用它。

      在C#中,通常在第一次调用类的任何成员之前执行静态构造函数。

      注意,静态构造函数没有访问修饰符,其它C#代码从来不调用它,但在加载类时,总是由.NET运行库调用它,所以像public,private这样的访问修饰符就没有任何意义。出于同样原因,静态构造函数不能带任何参数,一个类也只能有一个静态构造函数。很显然,静态构造只能访问累的静态成员,不能访问类的实例成员。

      无参数的实例构造函数与静态构造函数可以在同一个类中同时定义。虽然参数列表相同,但这并不矛盾,因为在加载类的时候执行静态构造函数,在创建实例时执行实例构造函数,所以何时执行哪个构造函数不会有冲突。

      如果任何静态字段有默认值,就在调用静态构造函数之前指定它们。

      下面演示静态构造函数的用法:

      class MainEntryPoint

        {

         static void Main()

         {

          Console.WriteLine("UserPreference:BackColor is " + UserPreference.BackColor.ToString());

         }

        }

      class UserPreference

        {

         public static readonly Color BackColor;

         static UserPreference()

         {

          BackColor = Color.Red;

         }

        private UserPreference()

        {

        }

       }

      该静态变量在静态构造函数中进行初始化。

      *从构造函数中调用其它构造函数

      有时,在一个类中有几个构造函数,这些构造函数包含一些共同的代码。

      class Car

      {

        private string des;

        private int nWheels;

        public Car(string des,int nWheels)

        (

          this.des = des;

          this.nWheels = nWheels;

        )

        public Car(string des)

        (

          this.des = des;

        this.nWheels = 4;

        )

      }

      这两个构造函数初始化了相同的字段,显然最好把所有的代码放在一个地方。C#有一个特殊的语法,称为构造函数初始化器,可以实现这个目的。

      class Car

      {

        private string des;

        private int nWheels;

        public Car(string des,int nWheels)

        (

          this.des = des;

        this.nWheels = nWheels;

        )

        public Car(string des):this(des,4)

        (

        )

      }

      这里,this关键字仅调用参数最匹配的那个构造函数。构造函数初始化器在构造函数的函数体之前执行。

      C#构造函数初始化器可以包含对同一个类的另一个构造函数的调用,也可以包含对直接基类的构造函数的调用,使用同样的语法,但应用base关键字代替this.初始化器中不能有多个调用。

      3.只读字段

      常量是一个包含不能修改的值的变量。但常量不必满足所有的要求。有时需要一些一些变量,其值不应改变,但在运行之前其值是未知的。C#为这种情形提供了另一种类型的变量:只读字段(readonly)。

      readonly关键字比const灵活得多,允许把一个字段设置为常量,但可以执行一些计算,以确定它得初始值。

      其规则是可以在构造函数中给只读字段赋值,但不能在其它地方赋值。只读字段还可以是一个实例字段,类的每个实例可以有不同得值。

      与const不同,如果要把只读字段设置为静态,就必须显示得声明它。

     

  • 相关阅读:
    P3371 【模板】单源最短路径(弱化版)
    村村通
    P1551 亲戚题解
    P4467 [SCOI2007]k短路
    P2483 【模板】k短路([SDOI2010]魔法猪学院)
    POJ——2449 Remmarguts' Date
    P1337 [JSOI2004]平衡点 / 吊打XXX
    P1118 [USACO06FEB]数字三角形`Backward Digit Su`…
    P1621 集合
    P1514 引水入城
  • 原文地址:https://www.cnblogs.com/afei-24/p/6698953.html
Copyright © 2011-2022 走看看