(C#) 精简 C# 入门(三)

最近在做一些需要用到 C# 开发的项目,许久未碰有些生疏,因此决定把语言的基础内容先都快速过一遍。为方便查阅,挑选最精简的部分整理于该博客。

【说明】

  1. 本系列的目标是整理出十个以内的博客(传送门),适合快速入门。目前更新到:精简 C# 入门(四)
  2. 本系列不适用于编程初学者(未接触任何计算机语言),这一类读者建议仔细学习【参考资料】中的视频资源

【参考资料】
1.b站:【01_C#入门到精通】新手强烈推荐:C#开发课程,一整套课程
2.知乎:C#入门到进阶资源汇总

#1 函数(方法)

1.1 声明 & 调用

函数的声明

[public] static 返回值类型 函数名([参数列表])
{
    
    
	// 代码块
}
  1. 我们将用 static 关键字修饰的函数称之为静态函数,相反的,如果不用则称之为非静态函数,这两者有相当显著的区别,详见 博客园:C#中静态与非静态方法比较
  2. 返回值类型除了 int, string 等之外,还可以使用空类型 void。返回值为空类型的函数可以不写 return 或者用 return 来表示函数的执行结束

(C#) 精简 C# 入门(一)Section 4 中的一段代码为例:

class Program
{
    
    
	public static int GetMax(int n1, int n2)
	{
    
    
	    return n1 > n2 ? n1 : n2;
	}
}

函数的调用

类名.函数名([参数列表])

示例:

int max = Program.GetMax(1, 2);

其他注意事项

对于一个函数而言,其功能单一并不是缺点,反而是基本要求。

1.2 参数 out, ref, params[]

out 输出参数: 使函数能够返回多个类型的值。语法:

[public] static 返回值类型 函数名([参数列表], out 返回值类型 变量名, ...)

示例:

class Program
{
    
    
    static void Main(string[] args)
    {
    
    
    	string str = null;
    	double n = Program.Test(519, out str);
    	
    	Console.WriteLine({
    
    0}, {
    
    1}", str, n);
    	Console.ReadKey();
    }

    static double Test(double n1, out string str1)
    {
    
    
        str1 = "Hello World";
        return n1 + 1
    }
}

>>>
"Hello World 520"

ref 引用参数:使函数内对变量的更改在函数外仍有效。语法:

[public] static 返回值类型 函数名([参数列表], ref 返回值类型 变量名, ...)

示例:

class Program
{
    
    
    static void Main(string[] args)
    {
    
    
    	int n = 0;
    	Program.Add_1(n);
    	Console.WriteLine(n);
    	Program.Add_2(ref n);
    	Console.WriteLine(n);
    	Console.ReadKey();
    }

	// 不使用 ref
    static void Add_1(int n1) {
    
     n1 += 1; }
    // 使用 ref
    static void Add_2(ref int n1) {
    
     n1 += 1; }
}

>>>
"0
1"

params[] 可变参数数组:将输入参数中与可变参数数组类型一致的元素,均当做数组的元素。可变参数数组只能作为输入参数列表中的最后一个元素,示例:

class Program
{
    
    
    static void Main(string[] args)
    {
    
    
    	Program.studentScore("小红", 98, 100, 99);
    	Program.studentScore("小白", 65, 32); // 第三次弃考无成绩
    	Console.ReadKey();
    }

    static void studentScore(string name, params int[] scores) 
    {
    
    
    	int sum = 0;
    	for (int i = 0; i < scores.Length; i++) {
    
     sum += scores[i]; }
    	Console.WriteLine("{0}'s all score: {1}", name, sum);
    }
}

>>>
"小红's all score: 297
小白's all score: 97"

当然,如果直接填 int[] 类型的数组也是 OK 的

1.3 重载 & 递归

重载:
重载指的是多个名称相同,但参数不同的函数(返回值类型不需要相同),使得计算机能够根据输入参数的类型以及个数,来选择相应的需要执行的函数。

示例:以下代码中对 Add 函数的重载使其既可以读取 double 类型的数据,也可以读取 string 来进行相加操作

class Program
{
    
    
    static void Main(string[] args)
    {
    
    
        double r1 = Add(1, 2);
        double r2 = Add("1", "2");
        Console.WriteLine("Add double: {0}", r1);
        Console.WriteLine("Add string: {0}", r2);
        Console.ReadKey();
    }

    static double Add(double n1, double n2)
    {
    
    
        return n1 + n2;
    }
    static double Add(string s1, string s2)
    {
    
    
        return Convert.ToDouble(s1) + Convert.ToDouble(s2);
    }
}

重载存在限制:

  1. 如果参数个数相同,那么参数类型就不能相同
  2. 如果参数类型相同,那么参数个数就不能相同

递归:
自己调用自己。示例:

class Program
{
    
    
    public static int currentTurn = 0;
    static void Main(string[] args)
    {
    
    
        Program.Repeat(3);
        Console.ReadKey();
    }

    static void Repeat(int maxTurn)
    {
    
     
        if (currentTurn < maxTurn)
        {
    
    
            Console.WriteLine("重复一次");
            currentTurn++;
            Program.Repeat(maxTurn);
        }
        return;
    }
}

>>>
"重复一次
重复一次
重复一次"

1.4 练习

关于函数的上述知识点,【参考资料】(1) 中的视频教程给了非常多很有帮助的练习,传送门:(以下红框内的视频中包含的练习都挺不错的)
在这里插入图片描述

#2 飞行棋游戏

仅使用当前所介绍的知识,就足矣写出一款飞行棋游戏,由于内容比较长,建议感兴趣的读者跳转到【参考资料】(1) 中的案例练习,传送门

作者根据自己的理解写了一个类似功能的游戏,目前还没有做代码优化,因此可以看到将近写了 400 行左右(就效果而言总体满意)。待优化一遍后应该会上传至 github,以下是代码截图以及运行截图:


#3 面向过程 → \to 面向对象

面向过程:分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现。

面向对象:把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

以上表述摘自 CSDN: 面向对象与面向过程的本质的区别,个人觉得非常直观易懂。

而在【参考资料】(1) 中,传送门,讲师也提供了几个能够帮助理解面向对象和面向过程差异的例子。例如,分别用这两者来描述“关门”:
在这里插入图片描述
我们使用 属性 + 方法 来描述对象。以三个对象,白炽灯、LED灯、小猫为例:

白炽灯
属性:尺寸(20’’)、价格(5 $)
方法:发光(黄色)
LED灯
属性:尺寸(18’’)、价格(9 $)
方法:发光(白色)
小猫
属性:种类(橘猫)、体重(10kg)、性格(闷骚)
方法:吃喝拉撒、与主人玩

上述三个例子当中,很显然白炽灯与LED灯这两个对象具有非常类似的属性和方法,我们把这样的对象进行进一步的封装,从而抽象出 “” 这一概念。

#4 类

类就是一个模子,确定了某种对象应该具有的属性和方法,因此对象是通过类来创建的,例如白炽灯与LED灯这两个对象可以由 “灯类” 创建 。

类的代码描述:

[public] class 类名
{
    
    
	Fields 字段;
	Properties 属性;
	Methods 方法
}

一个关于类的完整示例:

class Pet
{
    
    
    // 构造函数
    public Pet(string species, int age)
    {
    
    
        this.Species = species;
        this.Age = age;
    }
    public Pet(string species) : this(species, 0) {
    
     } // 重载

    // 字段
    private string _species;
    private int _age;

    // 属性
    public string Species {
    
     get => _species; set => _species = value; }

    public int Age
    {
    
    
        get => _age;
        set
        {
    
    
            value = value >= 0 ? value : 0;
            _age = value;
        }
    }

    // 方法
    /// <summary>
    /// 实例方法:介绍由该类创建的对象
    /// </summary>
    public void IntroObj()
	{
    
    
		Console.WriteLine("I'm a {0} years old {1}.", this.Age, this.Species);
	}

    /// <summary>
    /// 静态方法:介绍类
    /// </summary>
    public static void IntroClass()
    {
    
    
        Console.WriteLine("It's a class about pet, \nincluding some basic properties and functions.");
    }
}

以上代码中的新内容将在下列几个 subsection 中依次介绍。

4.1 字段 Fields

字段是属于类的变量,它表示了由类所创建的变量的某种属性。定义方式:

[private] 字段类型 字段名;

关于字段,我们需要知道:

  1. 关键字 private 则表示不可由外界访问
    关键字 public 表示可由外界访问(读取、修改)。为了字段的安全性,一般不使用 public
  2. 为了显示字段与方法中的变量的区别,字段名建议以 _ 开头
  3. 使用 this.字段名 的形式在类的内部调用字段,例如上例中的 this._age

4.2 属性 Properties

属性用于保护字段,对其读取、修改操作进行限制。属性由两个方法组成:

public 字段类型 属性名 {
    
     get => species; set => species = value; }

get() 表示读取
set() 表示修改

关于属性,我们需要知道:

  1. 属性一般都定义为 public
  2. 属性名其对应与字段名应该相同(含义相同),例如属性 Age 对应字段 _age
  3. 选中对应的字段后,使用快捷键 Ctrl+R+E 实现属性的快速生成(更加详细的步骤演示可参考这篇博客:【.NET】VS2017+C#如何快速生成属性
  4. 为了限制修改,一般情况下会对 set() 方法做一定的修改。
    例如在以下代码中,Age 属性就对年龄的修改做了限制,规定必须要大于等于 0
  5. 只读属性 & 只写属性:顾名思义,只读属性没有 set() 方法,只写属性没有 get() 方法

应用示例:

// 类内部
 class Pet
{
    
    
	private int _age;
	public int Age {
    
     
		get => _age;             
		set {
    
    
        	value = value >= 0 ? value : 0; // 限制对年龄的修改必须大于等于 0
	        _age = value;
        }
    }
	public void Intro()
	{
    
    
		// 读取。在类内部直接使用 this._age 也完全 OK
		Console.WriteLine("I'm {0} years old.", this.Age); 
	}
}

// 类外部
Pet cat = new Pet();
cat.Age = 2; // 修改
Console.WriteLine(cat.Age); // 读取

>>>
"2"

4.3 方法 Methods

4.3.1 静态 & 非静态

在学习类方法前,首先需要理解 “静态” (static) 与 “非静态” (也叫 “实例”) 之间的区别:

  1. 静态成员包含静态字段、静态属性、静态函数,实例成员同理
  2. 静态类中只能有静态成员,不能有实例成员
    非静态类中都能有
  3. 调用实例成员时,使用 对象名.实例成员名
    调用静态成员时,使用 类名.静态成员名
  4. 静态函数只能访问静态成员
    实例函数都能访问

在具体使用的时候,如果想要定义一个 “工具类”,可以考虑将其写成静态类。例如 C# 自带的 Console 类

4.3.2 构造函数

作用:初始化对象

public 类名([参数列表])
{
    
    
	// 赋值
}

示例:

public Pet(string species, int age)
{
    
    
    this.Species = species;
    this.Age = age;
}

构造函数当然也可以重载,在重载构造函数时为了减少代码冗余,可以使用 this 关键字(之前的已经介绍了 this 的两种用法 this.字段名this.属性名):

public 类名([参数列表1]):this([参数列表2]) {
    
     }

构造函数重载的对象一般为最完整的构造函数,因此需要在 参数列表1 加入一些默认初值来组成 参数列表2,例如下例中第二个构造函数将 age 默认为 0

public Pet(string species, int age)
{
    
    
    this.Species = species;
    this.Age = age;
}

public Pet(string species):this(species, 0) {
    
     }

4.3.3 析构函数

作用:在程序结束时会自动调用,帮助释放占用的内存资源

~类名() {
    
     }

示例:

~Pet() {
    
     }

猜你喜欢

转载自blog.csdn.net/weixin_43728138/article/details/115840788