方法
方法的结构
方法是一块具有名称的代码
方法是类的函数成员, 方法主要有两个部分, 方法头和方法体
void MyMethod()
{
Console.WriteLine("First");
Console.WriteLine("Last");
}
方法体的块中可以包含:
本地变量
控制流结构
方法调用
内嵌的块
static void Main()
{
int myInt = 3; //本地变量
while(myInt > 0) //控制流结构
{
--myInt;
PrintMyMessage(); //方法调用
}
}
本地变量
具有块作用域,无链接, 时间从声明到块结束的属性。
static void Main()
{
int myInt = 15;
SomeClass sc = new SomeClass();
...
}
实例字段从被创建开始, 到不被访问时结束, 具有默认值, 类的所有字段存储在堆中
本地变量没有隐式初始化, 值类型存储在栈中, 引用类型引用存储在栈中, 数据存储在堆中
类型推断和var关键字
var表示任何可以从初始化的右边推断出的类型
static void Main()
{
var total = 15;
var mec = new MyExcellentClass();
...
}
var只能用于本地变量,, 不能用于字段
只能在包含初始化的变量声明中使用。
嵌套块中的本地变量
C#无论嵌套级别如何, 都不能在嵌套块中声明相同名称的变量。
本地常量
在类型之加关键字const即可
const Type Identifier = Value;
控制流
与c基本相同
方法调用
call方法与invoke方法是同义的
class MyClass
{
void PrintdateAndTime();
{
DataTime dt = DataTime.Now; //获取当前的日期和时间
Console.WriteLine("{0}", dt);
}
static void Main()
{
MyClass mc = new Myclass();
mc.PrintDataAndTime();
}
}
返回值
与c相同
int GetHour()
{
DataTime dt = DataTime.Now;
int hour = dt.Hour; //获取小时数
return hour;
}
也可以返回用户定义类型的对象
MyClass method3()
{
MyClass mc = new MyClass ();
....
return mc;
}
class MyClass
{
public int GetHour()
{
DataTime dt = DataTime.Now;
int hour = dt.Hour;
return hour;
}
}
class Program
{
static void Main()
{
MyClass mc = new MyClass();
Console.WriteLine("Hour: {0}", mc.GetHour());
}
}
return; 后面不跟返回值返回void
class MyClass
{
void TimeUpdate()
{
DataTime dt = DataTime.Now;
if (dt.Hour < 12)
return;
Console.WriteLine("It's afternoon!");
}
static void Main()
{
MyClass mc = new MyClass();
pc.TimeUpdate();
}
}
参数
output参数除外, 其他参数都需要初始化, 其余与c相同
class MyClass
{
public int Sum(int x, int y)
{
return x + y;
}
public float Avg(float input1, float input2)
{
return (input1 + input2) \2.0F;
}
}
class Program
{
static void Main()
{
MyClass myT = new MyClass();
int someInt = 6;
Console.WriteLine
("Newsflash: Sum: {0} and {1} is {2}",
5, someInt, myT.Sum(5, someInt));
Console.WriteLine
("Newsflash : Avg : {0} and {1} is {2}", 5, someInt, myT.Avg(5, someInt));
}
}
引用参数
使用引用参数时必须在方法的声明和调用中都使用ref修饰符。
引用参数实参必须是变量
个人理解: 有点像查不到地址的指针, 说白了形参引用了实参的地址, 而不是值
class MyClass
{
public int Val = 20;
}
class Program
{
static void MyMethod(ref MyClass f1, ref int f2)
{
f1.val = f1.val + 5;
f2 = f2 + 5;
}
static void Main()
{
MyClass a1 = new MyClass();
int a2 = 10;
MyMethod(ref a1, ref a2);
}
}
输出参数
用于从方法体内部把数据传出到调用代码, 非常类似于引用参数,使用修饰符out表示
但是不需要在调用方法之前赋值, 而是在方法读取该参数之前必须被赋值
class MyClass
{
pblic int Val = 20;
}
class Program
{
static void MyMethod(out MyClass f1, out int f2)
{
f1 = new MyClass();
f1.val = 25;
f2 = 15;
}
static void Main()
{
MyClass a1 = null;
int a2;
MyMethod(out a1, out a2);
}
}
参数数组
参数数组允许** 零个或多个实参对应一个特殊的形参**
在数据类型之前使用params即可
需要在数据类型后放一个空的方括号
如果有, 必须是列表的最后一个(有点像c中的可变参数stdarg)
值参数的声明与调用不需要修饰符
引用参数和输出参数时需要修饰符
延伸式
这种形式调用中使用分离的实参
void ListInts(params int [] inVals){...}
...
ListInts();
ListInts(1, 2, 3);
ListInts(4, 5, 6, 7);
```csharp
class MyClass
{
public void ListInts(params int[] inVals)
{
if ((inVals != null) && (inVals.Length != 0))
for (int i = 0; i < inVals.Length; i++)
{
inVals[i] = inVals[i] * 10;
System.Console.WriteLine("{0} ", inVals[i]);
}
}
}
class Program
{
static void Main()
{
int first = 5, second = 6, third = 7;
MyClass mc = new MyClass();
mc.ListInts(first, second, third);
System.Console.WriteLine("{0}, {1}, {2}", first, second, third);
}
}
数组做实参
class MyClass
{
public void ListInts(params int[] inVals)
{
if ((inVals != null) && (inVals.Length != 0))
for (int i = 0; i < inVals.Length; i++)
{
inVals[i] = inVals[i] * 10;
System.Console.WriteLine("{0} ", inVals[i]);
}
}
}
static void Main()
{
int [] MyArr = new int[] {5, 6, 7}; //创建并初始化数组, 存在堆中
MyClass mc = new MyClass();
mc.ListInts(MyArr);
foreach (int x in MyArr)
Console.WriteLine("{0}", x);
}
方法重载
一个类中可以有一个以上的方法拥有相同的名称, 这称为方法重载(method overload)
使用相同名称的每个方法必须有一个和其它方法不相同的签名(signature)。
签名由以下组成
- 方法的名称
- 参数的数目
- 参数的数据类型和顺序
- 参数修饰符
形参的名称不是签名的一部分
命名参数
显式指定形参的名字, 就可以以任意顺序在方法调用中列出实参
class MyClass
{
public int Calc(int a, int a, int b, int c);
{return (a + b) * c}
static void Main()
{
MyClass mc = new MyClass();
int result = mc.Calc(c: 2, a: 4, b: 3);
Console.WriteLine("{0}", result);
}
}
调用时既可以使用位置参数也可以使用命名参数,但位置参数必须提前列出
可选参数
需要在方法声明时为参数提供默认值
class MyClass
{
public int Calc(int a, int b = 3)
{
return a + b;
}
static void Main()
{
MyClass mc = new MyClass();
int r0 = mc.Calc(5, 6);
int r1 = mc.Calc(5); //b使用默认值
Console.WriteLine("{0}, {1}", r0, r1);
}
}
所有必填参数必须在可选参数之前声明, 有params参数的必须在可选参数之后声明
栈帧
调用方法时, 内存从栈的顶部开始分配, 保存和方法关联的一些数据项, 这块内存称之为方法的栈帧(stack frame)。
方法退出时, 整个栈帧从栈上弹出, 也叫做栈展开(unwind)。
递归
计算阶乘数的代码如下
int Factorial(int inValue)
{
if (inValue <= 1)
return inValue;
else
return inValue * Factorial (inValue - 1);
}
class Program
{
public void Count(int inVal)
{
if (inVal == 0)
return;
Count(intVal - 1); //再一次调用
Console.WriteLine("{0}", inVal);
}
static void Main()
{
Program pr = new Program();
pr.Count(3);
}
}