学编程,来源栈;先学习,再交钱
当前系列: C#语法 修改讲义

复习:J&C:面向对象:构造函数 / 禁止继承 / 现代接口

类的声明

类文件

类文件.cs后缀指示编译器使用C#语法进行编译(如果代码以VB.NET书写,就以.vb为后缀

.cs文件名可以和类名不一致:本质上,.cs的文件名不影响类名,文件名和class冲突,以class后面定义的类名为准。

且一个.cs文件里也可以有多个public的classJava不行

internal

C#中的类默认内部的,仅可以在当前项目中使用。(演示:添加引用)

对应的修饰符是internal,可以显式的声明(Java没有这个访问修饰符)

部分类

可以使用partial关键字生成部分类(不是内部类):

partial class Student 
{ 
}

partial class Student
{
}

(同名的)部分类会在编译的时候被合成为一个类,所以我们完全可以把部分类当成一个类使用。

但这些都是非常规的做法,我们一般要使用,还是尽量一个类一个类文件。

静态类

另外,类上也用static修饰,这样的类被称之为

通常用于将静态方法归类。我们常用的Console和Math都是静态类:

public static class Console{ //....
public static class Math{    //....

静态类不能被实例化,其中只能有静态成员,不能有实例成员。(对比:Java中仅内部类上可以修饰static

namespace

C#要求(但默认是)名称空间和类文件所在文件夹路径必须对应。

别名

当不得不使用类的全名,且全名很长不方便的时候,还可以使用名称空间别名:

using Practise = CS.Senior.Practise;
using Theory = CS.Senior.Theory;
new Practise.Teacher();
new Theory.Teacher();

static

对于静态方法,还可以这样先声明:
using static System.Console;
然后直接调用其静态方法:
WriteLine();


字段修饰

只读(readonly)和常量(const)

他们都是只能读不能改的:

internal readonly string at = "源栈";
internal const string BELONG = "源栈";   //建议const名称全大写

但是,注意他们的语法区别:#常见面试题#

  • const一旦声明,就要赋值;readonly可用延后到constructor
    internal readonly string name;
    //internal const string BELONG;    //报错
    public Student(string name)
    {
        this.name = name;
    }
  • const的赋值必须是在编译时完成;readonly是在运行时赋值:
    internal readonly Teacher fg = new Teacher();
    //internal const Teacher xy = new Teacher();  //报错
    internal const Teacher xy = null; //OK
  • const默认static,由类名直接调用;readonly默认是实例成员,由对象调用
    Console.WriteLine(Student.BELONG);
    Console.WriteLine(new Student().name);

此外,const还可用于方法体内修饰变量,readonly不行。


构造函数

静态

有点特别:

internal class Student
{
    static Student()    //它不能有访问修饰符,也不能有参数
    {
    }
}

因为静态构造函数不能被开发人员调用,只能由.NET运行时在使用类之前,自动调用一次且仅仅调用一次:所以访问修饰符和参数对它都没有意义。

静态构造函数的调用时间,我们只需要掌握一点:很早,早在其他静态方法或构造函数之前就会运行,.NET会确保静态构造函数的运行(比如赋值)滞后于其他代码。断点演示

复制

实际开发中经常会有这样的代码:(复习构造函数重载

public Student(string name)
{
    this.name = name;      //代码行 1
}

public Student(string name, int age)
{
    this.name = name;      //代码行 2
    this.age = age;
}

代码行1和代码行2有些重复,是不是?所以我们可以这样写:

public Student(string name)   // 构造函数 1
{
    this.name = name;
}

public Student(string name, int age)
    : this(name)   // 使用this()调用构造函数 1
{
    this.age = age;
}

这里this代表当前对象的构造函数,然后按参数进行重载匹配。

这样,当运行Student(name, age)之前,会首先运行Student(name),这在构造函数内部的逻辑复杂,代码量大的时候尤其有用。


属性

C#一开始就专门定义了属性,如下所示:

private int _age;      //仍然需要字段_age
public int Age         //通常和字段同名,但首字母大写
{
    get { return _age; }    //当需要获取属性值的时候,返回字段_age的值
    set { _age = value; }   //当给属性赋值的时候,将值存放到_age中
}
然后,就可以像使用字段一样使用属性(虽然属性本质上是方法):
Student zjq = new Student();
zjq.Age = 25;                   //赋值,运行属性中的set{}
Console.WriteLine(zjq.Age);     //取值,运行属性中的get{}

当然,你也可以同时在set和get中添加逻辑。

表达式体

如果get只有一行的话,我们还可以使用表达式体:

private int _score;
public int Score
{
  get => _score;           //等同于: get { return _score; }
}     

自动属性

很多时候,我们其实不需要添加什么额外的逻辑。这种情况,C#为我们很贴心的设计了一个“语法糖(仅在语法层面上的细微改善)”:自动属性,如下所示:

        //注意不再需要_scroe字段
        public int Score { get; set; }
这样的写法,C#编译器会为我们自动的声明一个字段来存放属性的值。我们还可以:
public int Score { get; }       //让Score只读
//public int Score { set; }     //但不能让Score只写
//让Score外部可读,内部可写
public int Score { get; private set; }

#常见面试题:private set和没有set的区别#

  • 没有set,只能在构造函数,或者属性声明时赋值,其他地方不能再赋值
  • private set可以在任何地方(比如方法)赋值

但是,自动属性中,我们不能在set或get中添加使用任何逻辑。一旦添加逻辑,我们就像之前那样,配合字段使用属性。

声明时赋值

可以在属性声明时就直接赋值

我们还可以在实例化类的同时,给属性赋值(语法上公开字段也可以,但字段不建议暴露):

Student zjq = new Student()
{
    Name = "曾俊清",
    Age = 23,    //多个属性之间用逗号隔开
};

还可以“嵌套”:

Student zjq = new Student
{
    Name = "张俊清",
    Teacher = new Teacher
    {
        Majors = new string[] { "CSharp", "SQL"}
    }
};

调用的是无参构造函数,圆括号可以省略,

Student zjq = new Student
{

有参构造函数也可以直接传入构造函数参数:

Student zjq = new Student(28)
{

这种形式更进一步,就变成了

匿名类

即没有类名,也不需要声明,可以直接使用的类。比如:

var zjq = new  /*注意:没有类名了*/
{
    Name = "曾俊清",
    Age = 23,
};
Console.WriteLine(zjq.Name);

注意其语法特点:

  • 其变量类型只能用var,否则你也没法写
  • 所有属性,只能是只读的(readonly),就是说不能接下来再写个:zjq.Name="小曾";
  • 只是匿名而已,其实还是一个“类”,编译时会给名字(<AnonymousType>)
  • 拥有相同(名称+类型+次序)属性的匿名类会被当做同一个类
    var wx = new
    {
        Name = "王新",
        Age = 25,
    };   //再new一个匿名类给变量wx
    wx = zjq;  //wx和zjq之间可以互相赋值

PS:区别Java中的匿名类


索引

类似于属性,都是一种往对象里存值取值的方式。

但索引器通常用于封装具有多个元素的数组或集合(后文详述)。

其声明语法如下所示:

//也需要一个字段来实质上保存数据
private string[] _courses = { "SQL", "C#", "JavaScript" };
//和属性一样可以指定访问修饰符,需要指定返回类型
public string this[int index]
//不同:this关键字和[]运算符
//index还可以是其他类型,比如string
{
    //get和set的使用和属性几乎一样
    get { return _courses[index]; }
    //一样可以只读只写
    set { _courses[index] = value; }
}
  • 和属性相比,它没有名称,使用this
  • 而且多了一个[],里面指明索引器参数(有时又被成为“下标”)。参数必须要有类型和名字,可以有多个,多个参数见用逗号分开(和方法参数一样)

调用时:

Student wx = new Student();
Console.WriteLine(wx[1]);
  • 使用的运算符是中括号([])而不是点(.)
  • 必须有(至少一个)参数


作业

  1. 之前作业的基础上,继续用代码和调试过程演示:
    1. 值类型参数的引用传递
    2. 引用类型参数的引用传递
    3. return代替引用类型的引用传递
  2. 用自动属性/声明时赋值等C#语法糖重构之前作业
  3. 为之前的仓库类添加一个静态只读的字符串connection,以后可用于连接数据库
  4. 一起帮的求助可以有多个(最多10个)关键字,请为其设置索引器,以便于我们通过其整数下标进行读写。
  5. 声明一个令牌(Token)枚举,包含值:SuperAdmin、Admin、Blogger、Newbie、Registered,然后改造之前的TokenManager类

其他见:J&C:面向对象:构造函数(参数重载) / 内部类 / 继承(构造函数调用 & 禁止) / 现代接口

学习笔记
源栈学历
今天学习不努力,明天努力找工作

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 C#语法 中继续学习:

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码