博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【读书笔记】C#高级编程 第三章 对象和类型
阅读量:5244 次
发布时间:2019-06-14

本文共 9157 字,大约阅读时间需要 30 分钟。

(一)类和结构

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

类和结构的区别:内存中的存储方式、访问方式(类是存储在堆上的引用类型,结构是存储在栈的值类型)和它们的一些特征(如结构不支持继承)。

较小的数据建议使用结构来提高性能。

创建类使用 class 关键字

例子:

class ClassTest { }

创建结构使用 struct 关键字

例子:

struct StructTest { }

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

ClassTest classTest = new ClassTest();StructTest structTest = new StructTest();

 

 

(二)类

类中的数据和函数成为类的成员。

1、数据成员

数据成员是包含类的数据——字段、常量和事件的成员。

例子:

以下代码创建了一个名为People的类,其中包含了一个常量Country和一个字段Name

class People {    public const string Country = "中国";    public string Name;}

例子:

现在我们实例化People对象,可以使用Object.FieldName来字段,使用ClassName.ConstName来访问常量(因为常量总是静态)

static void Main(string[] args){    People people = new People();    people.Name = "张三";    Console.WriteLine("{0}的国家是{1}", people.Name, People.Country);    Console.ReadKey();}

运行以上代码,结果如下:

 

2、函数成员

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

  • 方法是与某个类相关的函数,与数据成员一样,函数成员默认为实例成员,使用static修饰符可以把方法定义为静态方法。
  • 属性是可以从客户端访问的函数组,其访问方式与访问类的公共字段类似。
  • 构造函数是在实例化对象时自动调用的特殊函数。它们必须与所属的类同名,且不能有返回类型。构造函数用于初始化字段的值。
  • 终结器类似于构造函数,但是在CLR检测到不再需要某个对象时调用它。它们的与所属类同名,但前有一个“~”符号。无法预测什么时候调用终结器。
  • C#允许指定已有的运算符应用于自己的类(运算符重载)。
  • 索引器允许对象以数组或集合的方式进行索引。

(1)方法

正式的C#术语区分函数和方法。

1.方法的声明

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

例子:

public bool IsLeft(string direction){    if (direction=="left")    {        return true;    }    else    {        return false;    }}

以上代码中,public是方法的可访问性,bool是返回值的类型,IsLeft是方法名,(string direction)是输入参数,{}内的是方法体

2.方法的调用

static void Main(string[] args){    Program program = new Program();    bool isLeft = program.IsLeft("left");    Console.ReadKey();}public bool IsLeft(string direction){    if (direction == "left")    {        return true;    }    else    {        return false;    }}

以上代码,因为IsLeft不是静态方法,所以要实例化所在的类,然后通过实例对象调用IsLeft

3.给方法传递参数

参数可以通过引用或通过值传递给方法。

例子:

参数是值类型还是引用类型的区别

static void Main(string[] args){    int _i = 0;    int[] _ints = { 0 };    Console.WriteLine("运行方法前:_ints[0]={0},_i={1}", _ints[0], _i);    SomeFunction(_ints, _i);    Console.WriteLine("运行方法后:_ints[0]={0},_i={1}", _ints[0], _i);    Console.ReadKey();}static void SomeFunction(int[] ints, int i){    ints[0] = 100;    i = 100;}

运行以上代码,结果如下:

4.ref参数

输入参数前带有 ref 关键字(使用 ref 的参数必须初始化),则该方法对变量所做的任何改变都会影响原始对象的值

static void Main(string[] args){    int _i = 0;    int[] _ints = { 0 };    Console.WriteLine("运行方法前:_ints[0]={0},_i={1}", _ints[0], _i);    SomeFunction(_ints,ref  _i);    Console.WriteLine("运行方法后:_ints[0]={0},_i={1}", _ints[0], _i);    Console.ReadKey();}static void SomeFunction(int[] ints,ref int i){    ints[0] = 100;    i = 100;}

运行以上代码,结果如下:

5.out参数

输入参数前面加上 out 关键字(可以不初始化),方法里把要输出的值直接赋给这个参数即可

static void Main(string[] args){    int _i;    SomeFunction(out _i);    Console.WriteLine("运行方法后:_i={0}", _i);    Console.ReadKey();}static void SomeFunction(out int i){    i = 100;}

运行以上代码,结果如下:

6.命名参数

命名参数允许按任意顺序传递。

static void Main(string[] args){    FillName("张","三");    FillName(lastName:"三",firstName:"张");    Console.ReadKey();}static void FillName(string firstName, string lastName){    Console.WriteLine(firstName + " " + lastName);}

运行以上代码,结果如下:

7.可选参数

当为参数提供了默认值时,参数就可选了。可选参数必须定义在参数列表的最后面

例子:

static void FillName(string firstName, string lastName="三"){    Console.WriteLine(firstName + " " + lastName);}

8.方法的重载

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

static void Show(string value){    Console.WriteLine(value);}static void Show(int value){    Console.WriteLine(value);}

(2)属性

属性的概念是:它是一个方法或一对方法,在客户端代码看来,它们是一个字段。

例子:

以下代码定义了一个名为Age的属性

public int age; public int Age{    get    {        return age;    }    set    {        ageage = value;    }}

我们采用C#区分大写的模式,使用相同的名称,但公有属性采用Pascal大小写形式命名,如果存在一个等价的私有字段,则它采用camel大小写形式命名。

1.只读和只写属性

在属性定义中省略set访问器,就可以创建只读属性;省略get访问器,就可以创建只写属性(一般不创建只写属性)

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

2.属性的访问修饰符

C#允许给属性的get和set访问器设置不同的访问修饰符,但必须有一个具备属性的访问级别。

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

3.自动实现的属性

如果属性的set和get访问器中没有任何逻辑,就可以使用自动实现的属性。

例子:

public int Age { get; set; }

使用自动实现的属性,就不能再属性设置中验证属性的有效性。虽然不能删掉get或set访问器,单可以给它们设置访问级别。

例子:

public int Age { get; private set; }

(3)构造函数

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

例子:

以下代码,创建了一个名为People的类,然后在其中创建了其无参的构造函数(当我们不提供任何构造函数时,编译器会在后台默认创建一个无参构造函数)

class People{    public People()    {    }}

以下代码,创建了一个有参数的构造函数,这个时候编译器就不会默认创建一个无参构造函数

class People{    string name;    public People(string Name)    {        name = Name;    }}

1.静态构造函数

静态构造函数没有参数,只执行一次,且一个类只能有一个静态构造函数,而前面的构造函数都是实例构造函数,只要创建类的对象就会执行它。

例子:

以下代码创建People类的静态构造函数

class People{    static People()    {    }}

2.构造函数中调用其他构造函数

使用 this 关键字调用同一类中的其他构造函数

class People{    public People(string firstName, string lastName)    {        Console.WriteLine(firstName + " " + lastName);    }    public People(string lastName):this("张",lastName)    {    }}

使用 base 关键字可以调用基类中的构造函数

class PeopleBase{    public PeopleBase(string firstName, string lastName)    {        Console.WriteLine(firstName + " " + lastName);    }}class People: PeopleBase{    public People(string lastName):base("张",lastName)    {    }}

 

3、只读字段

readonly关键字比const灵活得多,允许把一个字段设置为常量,但还需要执行一些计算,已确定它的初始值。其规则是可以在构造函数中给只读字段赋值,但不能在其他地方赋值。

例子:

class People{    public readonly string Country;    public People(string firstName)    {        if (firstName == "约翰")        {            Country = "外国";        }        else if (firstName == "张")        {            Country = "中国";        }    }}

 

 

(三)匿名类型

var与new关键字一起使用时,可以创建匿名类型。

例子:

以下代码创建了一个包含个人姓氏、名字和年龄的对象

var Person = new { firstName = "张", lastName = "三", age = "3" };

这个对象的类型名是编译器赋的一个我们无法使用名称,我们也不应该在类型名上去进行其他操作,因为会造成结果不同。

 

 

(四)结构

定义结构我们只需要用 struct 关键字替换类的 class 关键字即可

例子:

struct People{    public  string Name;}

需要注意:

结构是值类型。

结构不支持继承。

结构的无参构造函数总是由编译器提供,不可替换。

使用结构,可以指定字段如何在内存中布局

 

1、结构是值类型

虽然结构是值类型,但在语法上完全可以把它当做类来处理。但也因为结构是值类型,new 运算符与类和其他引用类型的工作方式不同,结构的new运算符并不分配堆中的内存,而是只调用相应的构造函数,根据传送给它的参数,初始化所有的字段。

例子:

以下代码完全合法(假定People在这里是结构)

People people;people.Name = "张三";

结构遵循其他数据类型都遵循的规则:在使用前所有的元素都必须进行初始化。

在结构上调用new运算符,或者给所有字段分别赋值,结构就完全初始化了。

结构主要用于小的数据结构。

 

2、结构和继承

结构不是为继承设计的。这意味着:它不能从一个结构中继承。每个结构都派生自ValueType。

 

3、结构的构造函数

为结构定义构造函数的方式与类相同,唯一的区别是结构不允许定义无参的构造函数。结构的无参构造函数有编译器提供,其作用是把数值初始化为0,引用类型初始化为null,即使提供了其他构造函数也是如此。

 

 

(五)弱引用

在应用程序代码内实例化一个类或结构时,只要有代码引用它,就会形成强引用。弱引用是使用WeakReference类创建的。

例子:

1 static void Main(string[] args) 2 { 3     WeakReference peopleReference = new WeakReference(new People()); 4     People people; 5     if (peopleReference.IsAlive) 6     { 7         people = peopleReference.Target as People; 8         people.Name = "张三"; 9         people.Age =12;10         Console.WriteLine("{0}今年{1}岁",people.Name,people.Age);11     }12     else13     {14         Console.WriteLine("引用为空");15     }16     GC.Collect();17     if (peopleReference.IsAlive)18     {19         people = peopleReference.Target as People;20     }21     else22     {23         Console.WriteLine("引用为空");24     }25     Console.ReadKey();26 }

 

 

(六)部分类

partial关键字允许把类、结构、方法或接口放在多个文件中。

partial class  People{    public  string Name;    public int Age;}partial class People{    public void Show() {        Console.WriteLine("{0}今年{1}岁", this.Name, this.Age);    }}

 

 

(七)静态类

如果类只包含静态的方法和属性,该类就是静态的。给类添加 static 关键字,编译器可以检查用户是否不经意间给该类添加了实例成员。

例子:

静态类的语法

static class People{    public static string Name;    public static int Age;    public static void Show() {        Console.WriteLine("{0}今年{1}岁", Name, Age);    }}

例子:

调用上面的静态类

static void Main(string[] args){    People.Name = "张三";    People.Age = 18;    People.Show();    Console.ReadKey();}

 

 

(八)Object类

所有的.NET类都派生自System.Object。

 

1、System.Object()方法

  • ToString()方法:是获取对象的字符串表示的一种便捷方式。
  • GetHashCode()方法:如果对象放在名为映射(也称为散射或字典)的数据结构中,就可以使用这个方法。
  • Equals()方法和ReferenceEquals()方法:用于比较对象相等性。
  • Finalize()方法:Object中的Finalize()方法其实什么也没做。
  • GetType()方法:这个方法返回从System.Type派生的类的一个实例。这个对象可以提供对象成员所属类的更多信息,包括基本类型、方法、属性等。System.Type还提供了.NET反射的入口点。
  • MemberwiseClone()方法:复制对象,但只是一个浅表复制,即复制了所有的值,但引用对象赋值的是引用地址。

 

2、ToString()方法

它是快速获取对象的字符串表示的最便捷的方式。

例子:

static void Main(string[] args){    int i = 100;    string str = i.ToString();    Console.ReadKey();}

运行以上代码,结果如下:

Obejct.ToString()声明为虚方法,所以可以在类中重写该方法

例子:

1 class Program 2 { 3     static void Main(string[] args) 4     { 5         People people = new People(); 6         people.Name = "张三"; 7         people.Age = 18; 8         Console.WriteLine(people.ToString()); 9         Console.ReadKey();10     }11 }12 13 14 class People15 {16     public string Name;17     public int Age;18     public override string ToString()19     {20         return string.Format("{0}今年{1}岁", this.Name, this.Age);21     }22 }

运行以上代码,结果如下:

 

(九)扩展方法

如果有类的源代码,继承就是给对象添加功能的好方法。但如果没有源码,则可以使用扩展方法,它允许改变一个类,但不需要该类的源代码。

扩展方法是静态的,它是类的一部分,但实际上没有放在类的源代码中。

例子:

以下代码创建了People类的一个扩展方法

static class PeopleExtension{    public static string AddCountryToPeople(this People people, string country)    {        return people.ToString() + "居住在" + country;    }}

调用以上扩展方法

static void Main(string[] args){    People people = new People();    people.Name = "张三";    people.Age = 18;    Console.WriteLine(people.AddCountryToString("中国"));    Console.ReadKey();}

运行以上代码,结果如下:

需要注意:

  • 调用扩展方法需要使用对象的实例。
  • 如果扩展方法名与源代码中的某个类名重复,那么扩展方法无效。

转载于:https://www.cnblogs.com/dlxh/p/6594007.html

你可能感兴趣的文章
设计软件AI学习体会及图片
查看>>
jQuery开发者眼中的AngularJS
查看>>
SpringMVC中使用Interceptor拦截器
查看>>
Can't connect to local MySQL server through socket '/tmp/mysql.sock'
查看>>
在Django中使用ORM创建图书管理系统
查看>>
高性能Javascrip 改变作用域链
查看>>
react面试宝典
查看>>
电脑上的windows键突然失灵了,肿么办
查看>>
linux命令:返回上一次目录
查看>>
利用 Chrome 原生工具进行网页长截图
查看>>
UVa LA 4254 - Processor 二分,贪心 难度: 1
查看>>
ANDROID笔记:利用XML布局文件生成Menu
查看>>
常用数据结构图--拓扑排序
查看>>
Oracle学习之buffer cache(copy过来的文本,待补充笔记)
查看>>
基数排序——浮点数结构体进阶
查看>>
在 linux 中 find 和 grep 的区别??
查看>>
减小数据泄密的九个办法
查看>>
在asp.net中使用自定义事件(带参数)
查看>>
计算购物车金额总和( jquery )
查看>>
Myeclipse快速排版的快捷键
查看>>