学编程,来源栈;先学习,再交钱
当前系列: 编程语言 所有题目 添加题目 修改



道可道,非常道;名可名,非常名。


静态 vs 实例

复习:

为什么要设计成单向访问?

从逻辑上讲,

  • 静态成员是属于类的(公有),所以当然也就是属于由类实例化而生成的所有对象的。而
  • 实例成员是属于每一个具体的对象的(独有),所以它既不能属于其他对象,也不能属于类——因为属于类也就属于其他对象了。

首先我们按照这个原则,来清理一下我们之前Student类所使用过的字段:

  • name(姓名)、age(年龄)、score(成绩),都应该是实例的,因为每个学生有不同的姓名、年龄和成绩
  • belong(属于哪个培训机构)、at(在哪里学习),可以是静态的,因为这个Student类里的每个学生都必然是在重庆飞哥的源栈学习的(如果这个Student定义的对象可以在其他地点其他培训机构学习,就不能使用static)。

所以,我们来看这段代码:

internal class Student
{
    static string at = "源栈";
    string _name;

    void learn(int score)
    {
        Console.WriteLine($"{_name}在{at}学习……");
        score++;
    }
}
实例方法learn()中使用了静态字段at,实例化一个学生,然后调用他的learn()方法是OK的:
    Student zjq = new Student("曾俊清");
            zjq.learn(95);

因为zjq(以及其他所有Student的对象)学习的地方确实就是Student类定义的源栈。

反之……反之不过来,因为学习应该是某个同学的学习,不能是一个类的学习。

深入C#底层,

  • 静态成员是属于类的,类不会被销毁,所以常驻内存的;一个类就是一个类,所以类的静态成员也是唯一的;
  • 实例成员是属于对象的,而对象是new出来的,同一个类可以new出若干个对象,这些对象是各自独立的,所以他们的成员也是各自独立的。


万物皆对象

面向对象其实就是映射现实。

@想一想@

  • 学生要参加某次考试,该如何映射?
    class Exam    //想到“考试”可以是个类
    {
        public void Attend()
        {
    
        }
    }
  • 参加考试,拿到一个成绩,如何体现?
    //返回的是Score类,不是int或float
    //传入的参数是Student,不是string
    public Score Attend(Student student)  
    {
    
    }
  • 每个学生有一个老师,这应该如何映射?
    class YzStudent
    {
        //不是string TeacherName
        public Teacher Teacher;
    
  • ……
从现在开始,转变思想:无论是字段、还是方法的参数和返回值,都可以/应该是对象

后面我们还会学到:(C#中)包括int、string、bool、数组……全都是类型,他们所对应的数据,全部都是对象。


能实例就不要静态

为什么呢?因为静态代表的是类,不能代表对象,所以不能体现出面向对象的优势。你看,我们说的是面向“对象”,而不是说的面向“类”。

前面我们讲过,引入面向对象,是为了“分门别类”的归纳管理数量众多的方法,但这只是原因之一,而且是最低层次的原因。更重要的是,我们可以通过对象(而不是类),进一步的优化我们的代码。

静态数据

几乎没办法传递,方法的参数和返回值,能够用静态类/数据么?

都是对象!

静态方法

以Student类为例,学习、成长啥的明细不适合静态,自己的事嘛;

比较适合的静态方法可以是“报到(enroll)”,把已经报到的学生信息存放起来:

//用一个静态数组存放所有入栈学生
static Student[] students = new Student[18];
存储所有同学的数组,应该是属于整个Student的,不应该属于任何一个学生。否则就会造成:
  • 一个同学enroll了两个同学,在他那里,源栈有两个同学;
  • 另一个同学enroll了三个同学,在他那里,源栈就有三个同学?!
//要逐渐习惯使用简单类型以外的自定义类型
public static void enroll(Student newbie)
{
    for (int i = 0; i < students.Length; i++)
    {
        //找到一个为null值的“空位”
        if (students[i] == null)
        {
            //把新同学放到空位中
            students[i] = newbie;
            return;
        }
    }
}

同时,从逻辑上来说,给新同学报到这件事,不应该任何一个同学都可以做,张三给李四报道?

交给Student的类来做比较合适,所以应该是static的(当然,你一定要把方法写成实例的也不违反语法,只是会怪怪的)


类的职责

好的方法有一个特点:参数个数要少。

还记得我们之前表示学生注册的enroll(Student)方法么?引入Student,将Student作为一种数据容器,减少方法参数个数,所以我们enroll()的参数就是一个Student对象。

但是,不要止步于此!

还能进一步的减少参数么?可以的:

void enroll(/*没有参数了*/)
{
    //使用自己的Name
    Console.WriteLine($"{Name}注册了");
}
这就是实例方法的典型应用:尽可能的用对象自有的数据来完成方法,而不是依赖于方法参数传递。
Student wx = new Student { Name = "王新" };
wx.enroll();

你可能觉得有点怪异:为什么自己可以给自己报到呢?

Good Question,这里的“可以”是一个关键词。当我们说一个对象/类“可不可以”干一件事的时候,究竟指的是什么?注意,“可不可以”靠的是“能力”,而不是“权限”。而所谓能力,就依靠于对象所拥有的“资源”即:数据和方法。

Student对象可以报到(enroll),或者说可以把enroll()方法放置到Student类中,是因为它拥有了报到所需要的资源,比如学生姓名、年龄、预缴订金等等,凭借这些数据(以及方法),就可以完成enroll()方法需要完成的功能。

一个对象的方法可以完全依赖于对象本身完成其功能,不带一个参数,这就是一种理想的面向对象的设计。

@想一想@

  • 用户发布一篇文章,怎么映射?
  • 学生每上一节课,老师获得1个积分,如何体现?


user.Publish(article) vs article.Publish()

问题的本质:发布一篇文章是谁的职责?

如何确定类/对象的职责?看它的能力(资源)。

假设文章发布时,要:

  • 用到文章的标题和正文,
  • 扣作者一个帮帮币。
@想一想@:User类有些什么资源,Article类呢?
class User
{
    public string name;
    public Article[] articles;  //为什么就不是static的呢?对比Student.enroll()
    public int bMoney;
class Article
{
    public string title;
    public User author;

核心在于Article里有没有,要不要User author:

  • 要,这种写法更优雅
    public void Publish()
    {
        author.bMoney--;
        Console.WriteLine(title);
    
        //article 添加到 articles
    }
    csharp.author = fg;
    csharp.Publish();  //内聚 vs 耦合
  • 不要,就只能这样写:
    public void Publish(Article article)
    {
        bMoney--;
        Console.WriteLine(article.title);
    
        //article 添加到 articles
    }
    fg.Publish(csharp);


作业

熟悉“一起帮”的功能:功能索引
  1. 观察“一起帮”的:
    1. 注册/登录功能,定义一个User类(以下所有类都按“一个类一个文件”的原则组织),包含字段:Name(用户名)、Password(密码)和 邀请人(InvitedBy),和方法:
      1. Register():能输出当前用户的注册信息,比如:fg开始注册,密码:1234,邀请人:dk
      2. Login():能输出当前用户的登录信息,比如:fg开始登录,输入密码:1234
    2. 求助版块,定义一个类Problem,包含字段:标题(Title)、正文(Body)、悬赏(Reward)和作者(Author)
    3. 帮帮币版块,定义一个类HelpMoney,表示一行帮帮币交易数据,包含你认为应该包含的字段和方法
    4. 每发布一个求助,该求助作者的帮帮币应该根据其悬赏金额予以冻结,这个过程应该实现在哪个类的哪个方法里面?
  2. 学生每上一节课,老师获得1个积分,如何体现?
  3. 考虑求助(Problem)的以下方法,哪些适合实例,哪些适合静态,然后添加到类中:
    1. Publish():发布一篇求助,并将其保存到数据库
    2. Load(int Id):根据Id从数据库获取一条求助
    3. Delete(int Id):根据Id删除某个求助
  4. 为这些类的字段和方法设置合适的访问修饰符。
  5. 实例化上述类,得到他们的对象:
    1. 给对象的字段赋值
    2. 调用对象的方法
面向对象 静态 实例

试一试:章节测试

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

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

在当前系列 编程语言 中继续学习:

我们的 特色

  • 先学习,后付费
  • 线上/线下,自由组合

更多了解

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

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