• 前言:类中的数据和函数称为类的成员。成员的可访问性可以是public protected internal protected、private或internal。

    1.数据成员:

    数据成员是包含类的数据(字段)、常量和事件的成员。数据成员可以是静态数据。类成员总是实例成员,除非用static进行显示的声明。字段是与类相关的变量。我们写代码先来表示一个类:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _1.类的结构的区别
    {
        public class PhoneCustomer2
        {
            public const string DayOfSendingBill = "Monday";        //发送账单的日期
            public int CustomerID;                                  //员工的ID号        
            public string FirstName;                                //
            public string LastName;                                 //
        }
    }

    一旦实例实例化了PhoneCustomer对象,就可以使用语法Object.FieldName来访问这些字段,如下实例所示:

           static void Main(string[] args)
            {
                //类的声明
                PhoneCustomer2 phoneCustomer2 = new PhoneCustomer2();
                //给字段进行赋值的操作
                phoneCustomer2.FirstName = "Simon";
            }

    常量与类的关联方式与变量与类的关联方式相同。使用 const关键字来声明常量。如果把它声明为public,就可以在类的外部进行访问它。

       public class PhoneCustomer2
        {
            public const string DayOfSendingBill = "Monday";        //发送账单的日期
            public int CustomerID;                                  //员工的ID号        
            public string FirstName;                                //
            public string LastName;                                 //
        }

    事件是类的成员,在发生某些行为(如改变类的字段或者属性,或者进行了某种形式的用户交互操作)时,它可以让对象通知调用方。客户包含所谓“事件处理程序”的代码来响应事件(事件在别的博客中进行详细的介绍)。 

    2.函数成员:

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

    2.1:方法是某个类相关的函数,与数据成员一样,函数成员默认是实例成员。使用static修饰符可以把方法定义为静态方法。

    2.2:属性是可以从客户端访问的函数组,其访问方式与访问类的公共字段类似。c#为读写类中的属性提供了专用的语法,所以不必使用那些名称中嵌有Get()或者Set()的方法。因为属性的这种方法不同于一般函数的语法,在客户端代码中,虚拟的对象被当作实际的东西。

    2.3:构造函数是在实例化对象是自动调用的特殊的函数。他们必须与所属的类同名。且不能有返回类型、构造函数用于初始化字段的值。

    2.4:终结器类似于构造函数,但是在CLR检测到不再需要某个对象时调用它。他们的名称与类相同。但是前面有一个“~”符号。不可能预测什么时候调用终结器(本章不做详细的解释)。

    2.5:运算符执行的最简单的操作就是加法与减法。在两个整数相加时,严格的说,就是对整数使用“+”运算符。c#还允许指定把已有的运算符应用于自己的类(运算符的重载)。

    2.6:索引器允许对象以数组或者集合的方式进行索引。

    2.7:方法:

    注意:正式的c#术语中区分函数与方法。“函数成员”不仅包含方法,也包含类或结构的一些非数据成员,如索引器、运算符、构造函数和析构函数,甚至还有属性。这些都不是数据成员。字段、常量、事件才是数据成员。

    2.7.1:方法的声明:

    在c#中,方法的定义包含任意方法的修饰符(如方法的可访问性)、返回值的类型,然后依次是方法名、输入参数的列表(用圆括号表示)和方法体(用花括号括起来)。实例如下:

    [访问修饰符]返回类型 方法名([参数] )
    {
      方法的主体
    }

     每个参数都包括参数的类型名和在方法体中的引用的名称。但如果方法有返回值,return语句就必须与返回值一起使用。以指定出口点:

            public bool IsSquare(Rectangle rect)
            {
                return (rect.Height == rect.Width);
            }

    如果没有返回值的话,就把返回类型指定为void,因为不能省略返回类型。如果方法不带参数,仍需要在方法名的后面包含一对空的圆括号(),此时return语句是可选的--当到达花括号时,方法会自动返回,注意方法可以包含任意多条return语句。

            /// <summary>
            /// 是否为正整数
            /// </summary>
            /// <param name="value">传入的值</param>
            /// <returns><返回true或者是false/returns>
            public bool IsPositive(int value)
            {
                if (value < 0)
                {
                    return false;
                }
                return true;
            }

    2.7.2:方法的调用:

    在下面的例子中,MathTest说明了类的定义和实例化、方法的定义和调用的语法。除了包含Main()方法的类之外,它还定义了MathTest,该类包含了几个方法和一个字段:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _1.类的结构的区别
    {
        /// <summary>
        /// 数学类
        /// </summary>
        public class ClassMathTest
        {
    
            private int _value;                     //数值字段的定义
    
            /// <summary>
            /// 得到相乘的结果
            /// </summary>
            /// <returns>返回最后的结果</returns>
            public int GetSquare()
            {
                return _value * _value;
            }
    
            /// <summary>
            /// 得到结果
            /// </summary>
            /// <param name="x">外部传入的值</param>
            /// <returns>得到结果</returns>
            public static int IntGetSquareOf(int x)
            {
                return x * x;
            }
    
            /// <summary>
            /// 得到数值
            /// </summary>
            /// <returns>返回数值</returns>
            public static double GetPi()
            {
                return 3.14159;
            }
    
        }
    }

    客户端程序的调用:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _1.类的结构的区别
    {
        class Program
        {
            static void Main(string[] args)
            {
    
                //类的实例化
                ClassMathTest classMathTest = new ClassMathTest();
    
                Console.WriteLine("调用静态的方法得到的值:{0}", ClassMathTest.IntGetSquareOf(5));
    
                Console.WriteLine("调用静态的方法得到的值:{0}", ClassMathTest.GetPi());
    
                //实例方法的调用
                Console.WriteLine("调用实例方法得到的值:{0}", classMathTest.GetSquare());
    
                Console.ReadKey();
    
            }
        }
    }

    结果如下:

    从代码中可以看出,MathTest类包含一个字段和一个方法,该字段包含一个数字,该方法计算该数字的平方。这个类还包含两个静态的方法。一个返回pi的值,另一个计算作为参数传入的数字的平方。

    这个类的功能并不是设计c#程序好例子,例如,GetPi()通常作为const字段来执行,而好的设计应使用目前还没有介绍的概念。

    2.7.3:给方法传递参数:

    参数可以通过值传递和引用传递的方式传递给方法。在变量通过引用传递给方法时,被调用的方法得到的就是这个变量,更准确的说,是指向内存中变量的指针。所以在方法内部对变量进行的任何的改变在方法退出后仍然有效。而如果变量通过值传递的方式传送给方法,被调用的方法得到的是变量的一个相同的副本。也就是说,在方法退出后,对变量进行修改会丢失。对于复杂的数据类型,按引用传递的效率更高,因为在按值传递的时候,必须赋值大量的数据。

    在c#中,除非特别指定,所有的引用类型都通过引用传递,所有的值类型都通过值传递。但是,在理解引用类型的含义时需要注意。因为引用类型的变量值包含对象的引用。作为参数传递的正是这个引用,而不是对象的本身。所以底层的对象的修改会保留下来。相反,值类型的变量包含的是实际的数据,所以传递给方法的是数据本身的副本。例如:int通过值传递给方法,对应方法对该int的值所做的任何的改变都没有改变原int对象的值。但是如果把数组或者其他引用类型(如类)传递给方法,对应的方法就会使用该引用改变这个数组的值,而新值会反射在原始数组对象上。我们下面例子说明参数的值类型的引用类型的区别:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _2.给方法传递参数
    {
        public class ParameterTest
        {
            /// <summary>
            /// 静态方法的调用
            /// </summary>
            /// <param name="ints">数组</param>
            /// <param name="i">数值</param>
            public static void SomeFunction(int[] ints, int i)
            {
                ints[0] = 100;
                i = 100;
            }
        }
    }


    客户端程序的调用:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _2.给方法传递参数
    {
        class Program
        {
            static void Main(string[] args)
            {
    
                int i = 0;
                int[] ints = { 0, 1, 2, 3, 4, 8 };
                Console.WriteLine("i={0}", i);
                Console.WriteLine("ints[0]={0}", ints[0]);
    
                //方法的调用
                ParameterTest.SomeFunction(ints, i);
                Console.WriteLine("i={0}", i);
                Console.WriteLine("ints[0]={0}", ints[0]);
                Console.ReadKey();
    
            }
        }
    }

    截图如下:

    这时候我们发现i的值保持不变,而在ints中改变的值在原始的数组中也改变了。注意字符串是不可变的(如果改变字符串的值,就会创建一个全新的字符串),所以字符串无法采用一般引用类型的行为方式。在方法的调用的时候,对字符串所做的任何的修改都不会影响到原始的字符串(在别的博客中做详细的介绍)。

    2.7.4:ref参数

    如前所述,通过值传送变量是默认的。也可以迫使值参数通过引用传递给方法。为此,要使用ref关键字。如果把一个参数传递给方法,且这个方法的输入参数中带有ref关键字,则该方法对变量所做的任何的改变都会影响到原始对象的值。

    这样我们对代码稍作修改(添加ref关键字):

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

    c#仍要求对传递给方法的参数进行初始化,理解这一点也非常重要。在传递方法之前,无论是按值传递,还是按引用传递,任何的变量都必须初始化。

    2.7.5:out参数:

    c#要求变量在被引用的前必须用一个初值进行初始化。尽管在吧输入变量传递给函数前,可以用没有意义的值初始化它们,因为函数将使用真实、有意义的值初始化他们,但是这样做是没有必要的。有时还会引起混乱。但是有一种方法能够简化c#编译器所坚持的输入参数的初始化。

    我们可以使用out参数来初始化。在方法的时候输入参数前面加上out前缀的时候,传递给该方法的变量可以不进行初始化。该变量通过引用传递。所以在从被调用的方法中返回时,对应方法对该变量进行的任何的改变都会保留下来。在调用该方法的时候,还需要使用out关键字。与在定义该方法时一样:

            public static void SomeFunction(out int i)
            {
                //在其方法的内部必须为其赋值
                i = 100;
            }


    方法的调用:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _2.给方法传递参数
    {
        class Program
        {
            static void Main(string[] args)
            {
                int j;
                ParameterTest.SomeFunction(out j);
                Console.WriteLine(j);
                Console.ReadKey();
            }
        }
    }

    3:属性:

    属性的概念:它是一个方法或者一对方法。在c#中我们可以这样定义属性:

            private string _name;
            public string Name
            {
                get { return _name; }
                set { _name = value; }
            }

    Get访问器不带任何的参数,且必须返回属性声明的类型。也不应为Set访问器指定任何显示参数,但是编译器假定它带有一个参数,其类型也和属性相同。并表示为Value。例如上面的代码中包含一个属性Age,它设置了一个字段age,在这个例子中,age表示属性Age的后备变量。

    在这里注意的是:我们采用c#的区分大小写模式,使用相同的名称,但共有属性采用Pascal大小写形式命名。如果存在一个等价的私有的字段,则它采用camel大小写形式命名。我们喜欢使用把下划线作为前缀的字段名,如 _age 这会为识别字段提供极大的便利。

    3.1:自动属性的实现:

    如果属性的set和get访问器中没有任何的逻辑。就可以使用自动实现的属性。这种属性会自动的实现后备成员变量。前面的Age实例的代码如下:

            public string Name { get; set; }

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

    使用了自动实现的属性,就不能再属性设置中验证实行的有效性。所以在上面的例子中,不能检验是否设置了无效的年龄。但是必须有两个访问器。尝试把该属性设置为只读属性,就会报错。但是,每个访问器的访问的级别可以不同。因此,下面的代码是合法的:

            public string Name { get; private set; }

     4:构造函数:

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

        public class Person
        {
            /// <summary>
            /// 构造函数的定义
            /// </summary>
            public Person()
            {
    
            }
        }

    一般的情况下,如果我们没有提供任何的构造函数的话,编译器会在后台创建一个默认的构造函数。这是一个非常基本的构造函数,它只能把所有的成员字段初始化为标准的默认值(例如,引用类型为空引用,数值数据类型为0,bool为false),这通常就足够了,否则就需要编写自己的构造函数了。构造函数的重载遵循与其他方法相同的规则。换言之,可以为构造函数提供任意多的重载,只是他们的签名有明显的区别:

            /// <summary>
            /// 构造函数的定义
            /// </summary>
            public Person()
            {
    
            }
    
            public Person(string name)
            {
    
            }

    但是如果提供了带参数的构造函数,编译器就不会自动提供了默认的构造函数。只是在没有定义任何构造函数的时候,编译器才会自动提供默认的构造函数。在下面的例子中,因为定义了一个带单个单数的构造函数,编译器会假定这是可用的唯一构造函数,所以他不会隐式的提供其他的构造函数:

        public class Person
        {
    
            private string _name;
            public string Name
            {
                get { return _name; }
                set { _name = value; }
            }
    
            public Person(string name)
            {
                this.Name = name;
            }
    
        }


    上面的代码还说明,一般的使用this关键字区分成员字段和同名的参数。如果试图使用无参数的构造函数实例化Person,就会得到一个编译错误。

    我们还可以构造函数定义为private和protected,这样不相关的子类就不能访问它了。

    5:静态构造函数:

    当然我们也可以给类编写无参数的静态的构造函数。这种构造函数只执行一次。而上面的构造函数是实例构造函数,只要创建类的对象,就会执行它。编写静态的构造函数的一个原因是:类有一些静态字段或者属性,需要在第一次使用类之前,从外部源中初始化这些静态字段和属性。

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

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

    下面用一个例子来说明静态构造函数的用法,为了简单起见,假定只有一个用户首选项——BackColor,它表示要早应用程序中使用的背景色。假定该首选项在工作日中的背景色是红色。在周末的时候是绿色。代码如下:

        public class UsePreferences
        {
    
            //字段为只读类型,只能在构造函数中设置
            public static readonly ConsoleColor BackColor;
    
            /// <summary>
            /// 静态的构造函数
            /// </summary>
            static UsePreferences()
            {
                //表示时间上的一刻
                DateTime now = DateTime.Now;
                //如果是星期六或者是星期日
                if (now.DayOfWeek == DayOfWeek.Saturday || now.DayOfWeek == DayOfWeek.Sunday)
                {
                    //则背景色为绿色
                    BackColor = ConsoleColor.Green;
                }
                else
                {
                    //否则为红色
                    BackColor = ConsoleColor.Red;
                }
            }
    
            /// <summary>
            /// 私有的构造函数
            /// </summary>
            private UsePreferences() { }
    
        }

    客户端代码的调用如下:

                Console.WriteLine("背景的颜色是:{0}", UsePreferences.BackColor.ToString());
    
                Console.ReadKey();

    6.从构造函数中调用构造函数(使用this关键字):

    有时,在一个类中有几个构造函数,以容纳某些可选参数,这些构造函数包含一些共有的代码。代码如下:

            private string _description;
            private uint _nWheels;
    
            public Car(string description, uint nWheels)
            {
                this._description = description;
                this._nWheels = nWheels;
            }
    
            public Car(string description)
            {
                this._description = description;
                this._nWheels = 4;
            }


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

            private string _description;
            private uint _nWheels;
    
            public Car(string description, uint nWheels)
            {
                this._description = description;
                this._nWheels = nWheels;
            }
    
            public Car(string description) : this(description, 0)
            {
    
            }

    客户端代码的调用:

            static void Main(string[] args)
            {
                Car aoDi = new Car("奥迪");
            }
  • 相关阅读:
    首页调单个产品分类的推荐产品,最新产品和热卖商品
    ecshop模板<! TemplateBeginEditable name="左上角主区域" >用法
    复制DataTable数据到新DataTable
    定时任务时间与当前时间比较的方法
    批量删除文件夹下包含指定字段的文件
    SQL 字符串去除空格函数
    Javascript的IE和Firefox(火狐)兼容性的常用例子
    查询表某列的加权平均值
    Jquery实现页面定时跳转
    Date.parse Firefox返回Nan的解决办法
  • 原文地址:https://www.cnblogs.com/MoRanQianXiao/p/7797697.html
Copyright © 2020-2023  润新知