C# 入门基础知识 - 方法
- 第8节 方法
- 8.1 C# 函数/方法简介
- 8.2 方法的声明及调用
- 8.2.1 参数列表方法的声明及调用
- 8.2.2 参数数组方法的声明及调用
- 8.2.3、引用参数与值参数
- 8.3 静态方法和实例方法
- 8.3.1 静态、实例方法的区别
- 8.2.3 静态、实例方法的声明及其调用
- 8.4 虚方法
- 8.4.1 虚方法介绍
- 8.4.2 虚方法的定义及调用
- 8.5 重写方法
- 8.6 外部方法
- 8.7 分部方法
- 8.8 方法的重载
- 8.9 Main方法
- 8.10 方法的综合使用
更多C#基础知识点可查看:C#学习笔记 - C#基础知识 - C#从入门到放弃
第8节 方法
8.1 C# 函数/方法简介
在C#中,方法是一段可重用的代码块,它执行特定的任务或功能。方法是面向对象编程中的基本概念之一,用于封装和组织代码。方法的使用包括:
1、方法的定义: 方法由方法体和方法签名组成。方法体是包含实际代码的部分,用于执行特定的任务。方法签名由方法的名称、返回类型、参数列表组成,用于唯一标识一个方法。方法签名中除了名称,还包括返回类型和参数的顺序、类型以及数量。
2、方法的调用: 方法可以通过方法名以及提供的参数值来调用。调用方法会执行方法体中的代码,并可能返回一个值。
3、方法的参数: 方法可以接受零个或多个参数,用于向方法传递数据。参数被定义为方法签名的一部分,它们有类型和名称。
4、方法的返回值: 方法可以返回一个值,也可以不返回任何值。方法的返回值类型在方法签名中进行指定。可以使用 void 返回类型来表示方法不返回任何值。
5、方法的重载: 在C#中,可以使用相同的方法名,但提供不同的参数列表,来定义多个具有相同名称但执行不同任务的方法。这被称为方法重载。
8.2 方法的声明及调用
方法由方法签名和方法体组成。方法签名包括方法的访问修饰符、返回类型、方法名和参数列表。
方法语句:
访问修饰符 返回类型 方法名(参数列表)
{
// 方法体
// 执行特定的任务
// 可以包含返回语句
}
方法语句说明:
1、访问修饰符(Access Modifier): 指定方法的可访问性,常用的访问修饰符有 public
、private
、protected
等。
2、返回类型(Return Type): 指定方法执行完后返回的数据类型,可以是基本类型、自定义类型或 void
(表示不返回任何值)。常用返回类型有void
、int
、double
类型等。
3、方法名(Method Name): 方法的名称,用于唯一标识一个方法。
4、参数列表(Parameter List): 传递给方法的参数,参数由类型和名称组成,多个参数之间用逗号分隔。
8.2.1 参数列表方法的声明及调用
方法语句:
访问修饰符 返回类型 方法名(参数列表)
{
// 方法体
// 执行特定的任务
// 可以包含返回语句
}
【示例】写一个方法,求两个是整数的和。
static int funcAdd(int num1,int num2)
{
return num1 + num2;
}
static void Main(string[] args)
{
//实现两个整数相加
Console.WriteLine("两个整数的和是:{0}", funcAdd(14,23));
Console.ReadKey();
}
列表参数要注意参数匹配:参数个数、参数类型、顺序一一对应。
8.2.2 参数数组方法的声明及调用
params
关键字用于在方法参数中指定可变数量的参数,可以在方法调用时传递任意数量的参数,而不需要显式地指定参数数组的大小。
要声明一个使用 params
的方法,需要在参数列表中使用 params
关键字,后跟一个数组类型的参数,语法如下:
访问修饰符 返回类型 方法名(params 参数类型[] 参数名)
{
// 方法体
// 执行特定的任务
}
【示例】写一个方法,计算任意个整数的和。
public int Sum(params int[] numbers)
{
int sum = 0;
foreach (int number in numbers)
{
sum += number;
}
return sum;
}
static void Main(string[] args)
{
//实现任意个整数相加
//调用方式1:
int sum1 = Sum(1,2,3,4,5,6,7,8,9,10);
Console.WriteLine("求和的结果:"+sum1);
//调用方式2:
int[] intArry = { 12, 23, 37, 28, 59 };
int sum2 = Sum(intArry);
Console.WriteLine("求和的结果:" + sum2);
Console.ReadKey();
}
8.2.3、引用参数与值参数
引用参数允许方法修改传递给它们的参数。在方法内部,对引用参数的任何修改都会影响到调用方法的代码所传递的参数值。
ref 参数
使用 ref 关键字可以将参数声明为 ref 参数,它将引用传递给方法。这意味着方法可以修改传递给 ref 参数的变量,并且这些修改在方法调用后仍然有效。
访问修饰符 返回类型 方法名(ref 数据类型 ref参数名)
{
// 修改ref参数的值
}
注意事项:
1、在方法声明和调用时都必须使用 ref 关键字。
2、引用参数必须在方法调用之前进行初始化。
3、引用参数可以修改原始变量的值。
【示例】求一个整数的平方值。
static void funcSquare(int num)
{
num *= num;
Console.WriteLine("num平方是:{0}", num);
}
static void Main(string[] args)
{
//求整数的平方值
int num = 10;
Console.WriteLine("num求平方前:{0}", num);
funcSquare(num);
Console.WriteLine("num求平方后:{0}", num);
Console.ReadKey();
}
运行程序:
num求平方前:10
num平方是:100
num求平方后:10
使用ref
关键字修改代码后:
static void funcSquare(ref int num)
{
num *= num;
Console.WriteLine("num平方是:{0}", num);
}
static void Main(string[] args)
{
//求整数的平方值
int num = 10;
Console.WriteLine("num求平方前:{0}", num);
funcSquare(ref num);
Console.WriteLine("num求平方后:{0}", num);
Console.ReadKey();
}
运行程序:
num求平方前:10
num平方是:100
num求平方后:100
通过运行两个代码结果可发现,执行完后ref关键字可使方法中计算后的值返回给参数变量并赋值。
out 参数
使用 out
关键字可以将参数声明为 out
参数。与 ref
参数不同,不需要在方法调用之前对变量进行初始化。
访问修饰符 返回类型 方法名(out 数据类型 out参数名)
{
// 修改out参数的值
}
注意事项:
1、调用方法时,需要使用 out
关键字显式标记传递的参数为 out
参数。
2、在方法内部,必须在方法结束之前为 out
参数分配一个值。
值参数是将数据作为副本传递给方法的参数类型。在方法内部对值参数的任何修改都不会影响到调用方法的代码所传递的原始参数值。
访问修饰符 返回类型 方法名(数据类型 参数名)
{
// 修改参数的值不会影响原始值
}
注意事项:
1、无需使用关键字声明值参数。
2、方法操作的是参数值的副本,而不是原始变量。
3、值参数不会修改原始变量的值。
【示例】求数组内最大值和其索引值。
程序代码:
//求数组中最大值并返回值和索引值
static int funcMaxNum(int[] nums,out int maxNumIndex)
{
int maxNum = nums[0];
maxNumIndex = 0;
for (int i=0;i<nums.Length;i++)
{
if (maxNum < nums[i])
{
maxNum = nums[i];
maxNumIndex = i;
}
}
return maxNum;
}
static void Main(string[] args)
{
//求数组中的最大值和其索引值
int[] intArray2 = { 12, 34, 26, 29, 15, 43, 6, 17, 29, 35 };
int maxNumIndex;
Console.WriteLine("数组intArray2最大值是:{0},其索引值是:{1}",funcMaxNum(intArray2,out maxNumIndex),maxNumIndex);
Console.ReadKey();
}
运行程序:
数组intArray2最大值是:43,其索引值是:5
总结:
out
关键字和 ref
关键字都用于传递参数给方法,并且在方法内部能够修改参数的值。但是它们在使用上有一些不同之处:
1、ref
关键字:
① 调用方法时,使用 ref
关键字将传递的参数标记为引用参数。
② 被 ref
标记的参数在进入方法之前必须被初始化,否则会引发编译错误。
③ 在方法内部,对 ref
参数的任何修改会影响到调用方法的代码所传递的原始参数值。
④ 可以将 ref
用于值类型和引用类型的参数。
2、out
关键字:
① 调用方法时,使用 out
关键字将传递的参数标记为输出参数。
② out
参数无需在进入方法之前进行初始化。
③ 在方法内部,必须在方法结束之前为 out
参数分配一个值,否则会引发编译错误。
④ 类似于 ref
参数,对 out
参数的修改会影响到调用方法的代码所传递的原始参数值。
⑤ 可以将 out
用于值类型和引用类型的参数。
3、异同之处:
① ref
和 out
关键字都用于引用参数,允许方法修改传递给它们的参数的值。
② ref
要求在进入方法之前对参数进行初始化,而 out
则无需初始化。
③ 对于 ref
和 out
参数的修改会影响到调用方法的代码所传递的原始参数值。
在选择引用参数和值参数时,应该根据具体的需求和设计考虑。如果需要在方法内部修改传递的参数,并使修改对原始变量生效,那么应该使用引用参数。如果只需要对传递的参数进行传递和使用,而不影响原始变量的值,那么应该使用值参数。
8.3 静态方法和实例方法
8.3.1 静态、实例方法的区别
C#中的方法可以分为静态方法(static methods)和实例方法(instance methods)。这两种方法之间有以下区别:
1、访问方式:
静态方法:可以通过类名直接调用,不需要创建类的实例。
实例方法:需要通过已创建的对象或实例来调用。
2、执行方式:
静态方法:在程序加载时进行静态绑定,只有一份,可以在程序的任何地方直接调用。
实例方法:需要通过对象的引用进行动态绑定,每个对象都有自己的实例方法。
3、对象依赖:
静态方法:不依赖于特定的对象实例,无法访问实例成员,只能访问静态成员。
实例方法:依赖于特定的对象实例,可以访问该对象实例的成员变量和方法。
4、内存占用:
静态方法:在内存中只有一份,与类的实例无关。
实例方法:每个对象实例都有自己的方法拷贝,会占用更多的内存。
5、生命周期:
静态方法:与类的生命周期相同,在程序的整个执行期间都存在。
实例方法:随着对象的创建和销毁而生命周期的产生和结束。
6、适用场景:
静态方法适用于不依赖于对象状态,仅与类的行为或数据相关的功能。例如,数学计算工具类中的数学函数。
实例方法适用于需要使用对象属性和行为的场景,每个对象可以有自己的状态和数据。
8.2.3 静态、实例方法的声明及其调用
int exampleVar = 0; //实例成员
static int staticVar = 0; //静态成员
static void staticMethod() //静态方法
{
//exampleVar = 1; 无法对实例成员赋值
staticVar = 2;
Console.WriteLine("这是一个静态方法示例。");
Console.WriteLine("静态方法只能给静态成员赋值:staticVar = {0},无法给实例成员赋值。",staticVar);
}
void exampleMethod() //实例方法
{
exampleVar = 1;
staticVar = 2;
Console.WriteLine("这是一个实例方法示例。");
Console.WriteLine("实例方法可以给实例成员赋值:exampleVar = {0},也能给静态成员赋值:staticVar = {1}。",exampleVar, staticVar);
}
static void Main(string[] args)
{
//静态方法调用
//1、直接调用
staticMethod();
//2、通过Program调用
Program.staticMethod();
//实例方法调用
//1、类的实例化
Program p = new Program();
//2、通过p类调用
p.exampleMethod();
Console.ReadKey();
}
运行程序:
这是一个静态方法示例。
静态方法只能给静态成员赋值:staticVar = 2,无法给实例成员赋值。
这是一个静态方法示例。
静态方法只能给静态成员赋值:staticVar = 2,无法给实例成员赋值。
这是一个实例方法示例。
实例方法可以给实例成员赋值:exampleVar = 1,也能给静态成员赋值:staticVar = 2。
8.4 虚方法
8.4.1 虚方法介绍
在C#中,virtual
关键字用于声明一个方法为虚方法(virtual method)。虚方法是一种可以被子类重写的方法,允许在派生类中自定义其行为,实现多态性。
使用虚方法需要满足以下条件:
1、在基类中声明方法时使用 virtual
关键字。
2、对于虚方法的实现,可以在派生类中使用 override
关键字来重写基类中的虚方法。
8.4.2 虚方法的定义及调用
虚方法和非方法的对比:
using System;
namespace 虚方法
{
//新建一个class1类
class class1
{
//默认方法被指定为私有的(private),只能在当前类中进行访问
//如果希望在其他类中被使用,需将其指定为公有的(public)
//public访问权限最高,只要在项目内都可以进行访问
public virtual void virtualMehtod() //虚方法可在派生类中被重写
{
Console.WriteLine("1、这是一个虚方法示例。");
}
public void notVirtualMehtod()
{
Console.WriteLine("2、这是一个非虚方法示例。");
}
}
//创建一个class2类用来继承class1中的方法
class class2:class1 //class2继承class1
{
//重写class1中的虚方法
public override void virtualMehtod()
{
Console.WriteLine("3、这是重写的虚方法示例。");
}
public new void notVirtualMehtod()
{
Console.WriteLine("4、这是一个新的非虚方法。");
}
}
class Program
{
static void Main(string[] args)
{
//class1中方法的调用,调用前需将类实例化
class1 c1 = new class1();
c1.virtualMehtod();
c1.notVirtualMehtod();
//class2中方法的调用,调用前需将类实例化
class2 c2 = new class2();
c2.virtualMehtod();
c2.notVirtualMehtod();
//将class2中的方法赋值给class1
c1 = c2;
c1.virtualMehtod();
c1.notVirtualMehtod();
Console.ReadKey();
}
}
}
运行程序:
1、这是一个虚方法示例。
2、这是一个非虚方法示例。
3、这是重写的虚方法示例。
4、这是一个新的非虚方法。
3、这是重写的虚方法示例。
2、这是一个非虚方法示例。
通过执行结果可发现:
虚方法可以在基类中提供一个默认实现,派生类可以选择重写该方法。非虚方法在基类中定义后,无法在派生类中进行修改。
8.5 重写方法
在C#中,方法的重写是使用 override
关键字来在派生类中对基类中的虚方法进行重新定义,以提供自定义的实现。方法重写使得在运行时可以根据对象的实际类型进行动态调用,实现多态性。
要实现方法的重写,需要满足以下条件:
1、基类中的方法必须使用 virtual
关键字进行声明,表示该方法可被派生类重写。
2、派生类中的方法必须使用 override
关键字进行声明,表示要对基类中的方法进行重写。
3、重写的方法的名称、参数列表和返回类型必须与基类中的方法一致。
【重写方法示例】
using System;
namespace 重写方法
{
//新建一个类存放方法
class class1
{
public virtual void virtualMehtod()
{
Console.WriteLine("1、这是class1的一个虚方法,可以被重写。");
}
}
//新建一个继承类用于重写方法
class class2 : class1
{
//重写方法 - 签名相同即方法的定义名称关键字等相同
public override void virtualMehtod()
{
Console.WriteLine("2、这是class2重写class1的虚方法,被称为已经重写了的基方法。");
}
}
//如果不想让继承的类去重写virtualMehto()方法,可采用关键字sealed
class class3 : class2
{
public override void virtualMehtod()
{
Console.WriteLine("3、这是class3重写class2的虚方法。");
}
}
class Program
{
static void Main(string[] args)
{
class1 c1 = new class1();
c1.virtualMehtod();
class2 c2 = new class2();
c2.virtualMehtod();
class3 c3 = new class3();
c3.virtualMehtod();
Console.ReadKey();
}
}
}
运行程序:
1、这是class1的一个虚方法,可以被重写。
2、这是class2重写class1的虚方法,被称为已经重写了的基方法。
3、这是class3重写class2的虚方法。
如果不想方法被重写,可使用 sealed
关键字:
8.6 外部方法
在C#中,外部方法(external method)指的是在当前程序集(assembly)之外定义的方法。外部方法可以是在其他程序集、第三方库或者操作系统提供的函数等。
通过以下两种方式来调用外部方法:
1、Platform Invoke(P/Invoke):
使用DllImport特性(Attribute)和相关的参数声明方式,在C#中声明和调用外部方法。
【示例】:
using System.Runtime.InteropServices;
namespace 外部方法
{
//方法的位置:通常位于类当中,并且与其他方法保持平级关系
class Program
{
//使用之前应该引入命名空间:System.Runtime.InteropServices
[DllImport("User32.dll")]
//声明外部方法 使用关键字extern由于配合DLLImport属性使用,所以必须包含static关键字
public static extern int MessageBox(int h, string m, string c, int type);
static int Main(string[] args)
{
Console.Write("请输入姓名:");
string name = Console.ReadLine();
//利用return进行弹出对话框,所以需要将Main方法改为有返回值
return MessageBox(0, "你好!" + name + "\n\n" + "好好学习C#,努力工作", "弹窗提示", 0);
}
}
}
运行程序:
上面的示例使用了 P/Invoke
方式调用了 Windows 操作系统中的 MessageBox
方法,通过 DllImport
特性声明了 MessageBox
方法,并在 Main
方法中进行了调用。
2、COM(Component Object Model):
使用 COM 互操作性功能,通过调用 COM 组件中的方法来实现与外部方法的交互。
【示例】:
using System;
namespace 外部方法
{
class class1
{
static void Main()
{
Type type = Type.GetTypeFromProgID("Excel.Application");
dynamic excelApp = Activator.CreateInstance(type);
excelApp.Visible = true;
excelApp.Workbooks.Add();
excelApp.Cells[1, 1].Value = "Hello, World!";
excelApp.Cells[2, 1].Value = "Hello, C#~~";
excelApp.Cells[3, 1].Value = "这是一个外部方法通过COM组件实现的实例。";
excelApp.Quit();
excelApp = null;
}
}
}
运行程序:
上面的示例通过 COM 互操作性功能,使用了 Office Excel COM 组件中的方法来打开 Excel 应用程序,并在设定单元格内写入了 "Hello, World!"等内容。
需要注意:
调用外部方法需要确保相关的动态连接库(DLL)文件或者 COM 组件可在运行时访问,并将其正确加载。并且需要根据外部方法的参数声明和返回类型进行正确的数据类型匹配和转换。
总结:
在 C# 中,可以使用 P/Invoke 或者 COM 互操作性功能来调用外部方法。通过使用相关的特性或者 COM 组件的方式,可以在 C# 中声明和调用其他程序集、第三方库或者操作系统提供的方法。
8.7 分部方法
C#中,分部方法(Partial methods)是一种特殊类型的方法,它允许将方法的实现分散在多个部分(partial)中。分部方法被用于在一部分代码中定义方法的签名,而在另一部分代码中提供方法的实现。这对于代码生成、框架和工具生成代码或添加额外的逻辑很有用。
以下是分部方法的特点和用法:
1、分部方法必须在同一个声明部分(partial declaration)中进行定义和实现,一个部分声明了方法的签名,而另一个部分提供了实际的实现。分部方法不能跨越不同的部分。
2、分部方法只能包含 void 返回类型。
3、分部方法必须使用 partial 关键字来进行标记。
4、分部方法的实现是可选的,这意味着可以只定义分部方法的签名而不提供实现。未提供实现的分部方法在编译时会被自动移除,对调用这些分部方法的代码没有任何影响。
5、分部方法只能在包含它的部分声明所在的类或结构中进行调用。
【示例】
using System;
namespace 分部方法
{
public partial class Program
{
//声明与定义一个分部类
//声明分部方法
//方法默认为私有,也可以手动加上private
partial void Write(); //声明
partial void Write() //分部方法定义
{
Console.WriteLine("这是一个分部方法");
}
}
public partial class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.Write();
Console.ReadKey();
}
}
}
运行程序:
这是一个分部方法
需要注意:
1、没有提供实现的分部方法会在编译时被自动移除,所以在调用这样的分部方法时要确保实现已经提供。
2、分部方法能够使代码更加模块化和可维护,可以在不改变原有代码结构的情况下,通过添加分部方法的实现来添加额外的行为或逻辑。
8.8 方法的重载
在C#中,方法的重载(Method Overloading)是指在同一个类中使用相同的方法名但具有不同的参数列表来定义多个方法。这样做可以根据不同的参数类型或参数个数来选择调用合适的方法。
在方法重载中,方法的签名是由方法名和参数列表组成的。一个方法的参数列表包括参数的类型、参数的个数和参数的顺序。
通过方法重载,可以为相似的功能提供更加灵活的调用方式,提高代码的可读性和可维护性。
【示例】 根据参数的个数判断所求类型的面积。
using System;
namespace 方法重载
{
class Program
{
//需要在同一个类中
//求圆的面积
static void WriteArea(int radius)
{
double area = System.Math.PI * radius * radius;
Console.WriteLine("圆的面积是:"+area);
}
//求矩形面积
static void WriteArea(int width,int length)
{
int area = width * length;
Console.WriteLine("矩形的面积是:"+area);
}
//求三角形面积
static void WriteArea(int a, int b,int c)
{
double p = (a + b + c) / 2;
double area = System.Math.Sqrt(p*(p-a)*(p-b)*(p-c));
Console.WriteLine("三角形的面积是:" + area);
}
static void Main(string[] args)
{
//利用方法重载进行调用方法
WriteArea(4);
WriteArea(6, 9);
WriteArea(3, 4, 5);
Console.ReadKey();
}
}
}
运行程序:
圆的面积是:50.2654824574367
矩形的面积是:54
三角形的面积是:6
8.9 Main方法
在C#中,Main
方法是一个程序的入口点(Entry Point),它是程序执行的起始位置。当程序运行时,它会从 Main
方法开始执行。
Main
方法必须是一个静态方法(Static Method),因为它在应用程序启动时没有面向对象的实例。它也必须位于一个类或结构中,并且是程序的唯一一个入口点。
Main
方法有以下几种常见的声明方式:
1、基本声明方式:
static void Main()
{
// 程序代码
}
这是最简单的 Main
方法声明方式,它的返回类型是 void
,不接受任何参数。
2、接受命令行参数的声明方式:
static void Main(string[] args)
{
// 程序代码
}
这种声明方式的 Main
方法接受一个字符串数组参数 args
,该参数包含在命令行中传递给程序的参数。通过 args
参数,可以处理命令行传入的参数信息。
3、带有返回值的声明方式:
static int Main()
{
// 程序代码
return 0; // 返回一个整数值,通常用于表示程序的退出状态
}
在某些情况下,Main
方法会返回一个整数值,作为程序的退出状态码。返回值为 0
表示程序正常退出,其他返回值可用于表示不同的错误或状态。
需要注意:
只有 Main
方法是程序的入口点,其他方法需要在 Main
方法中进行调用才能执行。
示例:
using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
Console.ReadKey(); //等待用户按下任意键
}
}
在上面的示例中,Main
方法被声明为接受一个字符串数组参数 args
,并在控制台输出 “Hello, World!”。程序运行时,会在控制台显示这条消息,并等待用户按下任意键才会退出程序。
总结:
Main
方法是程序的入口点,用于指定程序的起始位置。它必须是一个静态方法,在一个类或结构中进行声明。Main
方法有多种声明方式,可以不带参数、带有命令行参数或带有返回值。
8.10 方法的综合使用
【示例】输入一个整数,要求输出其加法表和乘法表。
程序代码:
using System;
namespace 方法的综合使用
{
class Program
{
//乘法表方法的实现
static void Multiple(int num)
{
Console.Write("\n{0}的乘法表:",num);
for(int i = 1; i <= num; i++)
{
if (num % i != 0)
continue;
else
{
Console.Write("{0}*{1}={2} ",i,num/i,num);
}
}
}
//加法表方法的实现
static void Add(int num)
{
Console.Write("{0}的加法表:", num);
for (int i = 0; i <= num/2; i++)
{
Console.Write("{0}+{1}={2} ", i, num - i, num);
}
}
static void Main(string[] args)
{
//实现用户输入数字的加法表与乘法的输出
//输入:12
//输出加法:0+12=12 1+11=12 2+10=12 ...6+6=12
//输出乘法:1*12=12 2*6=12 3*4=12 ... 12*1=12
Console.Write("输入一个整数:");
int num = int.Parse(Console.ReadLine());
Add(num);
Multiple(num);
Console.ReadKey();
}
}
}
运行程序:
输入一个整数:12
12的加法表:0+12=12 1+11=12 2+10=12 3+9=12 4+8=12 5+7=12 6+6=12
12的乘法表:1*12=12 2*6=12 3*4=12 4*3=12 6*2=12 12*1=12