第一个概念,将方法和事件(event)区分开,事件是特殊的委托(delegate),将方法作为参数传递,
而方法(函数)则是:包含一系列语句的代码块。 程序通过“调用”方法并指定所需的参数来执行语句。 在 C# 中,每个执行指令都是在方法的上下文中执行的。
第二个概念,方法是面向过程(结构)的编程思想,主要用于实现代码的复用,消除冗余代码。
方法的声明:关注访问修饰符、返回类型和参数列表
访问修饰符有:public、internal、protected、privat
方法的返回类型可以是:void(没有返回值)、基本类型(预定义类型)、自定义类型
void Main() { Print(); Show("rammderek"); Console.WriteLine (GetMessage()); Console.WriteLine (GetMessage("rammderek")); Console.WriteLine (Add(Add(Add("这是一个","有多个参数,"),"也有返回类型"),"的方法")); GetBook("C#","Microsoft",10.99M,DateTime.Now).Dump(); } //没有返回值,无参数 public void Print(){ Console.WriteLine ("这是一个没有返回值,没有参数的方法"); } //没有返回值,有参数 public void Show(string name){ Console.WriteLine ("你好,{0}!这是一个没有返回值,但是有参数的方法", name); } //有返回值,无参数 public string GetMessage(){ return "这是一个有返回值,但是没有参数的方法"; } //有返回值,有参数 public string GetMessage(string name){ return string.Format("你好,{0}!这是一个有返回值,也有参数的方法", name); } //有返回值,有多个参数 public string Add(string prefix,string suffix){ return prefix + suffix; } //返回自定义类型 public Book GetBook(string title,string author,decimal price,DateTime pubdate){ return new Book{ Title=title,Author=author, Price=price, PubDate=pubdate}; } //自定义图书类型 public class Book{ public string Title { get; set; } public string Author { get; set; } public decimal Price { get; set; } public DateTime PubDate { get; set; } }
方法的参数有以下几种:
值类型参数:不会影响方法外部的值类型对象,但是会印象引用类型对象(字符串除外)
void Main() { var m = 3; Add(m); //该方法不会影响变量m的值 Console.WriteLine ("方法执行完毕后,变量的值是:{0}",m); var s = "Hello World"; ToUpper(s); //该方法不会影响变量m的值 Console.WriteLine ("方法执行完毕后,变量的值是:{0}",s); var book = new Book{ Title="HTML" }; ChangeBook(book); //该方法会影响变量m的值 Console.WriteLine ("方法执行完毕后,变量的值是:{0}",book.Title); } public void Add(int n){ n++; Console.WriteLine ("在方法中,变量的值是:{0}",n); } public void ToUpper(string message){ message = message.ToUpper(); Console.WriteLine ("在方法中,变量的值是:{0}",message); } public void ChangeBook(Book book){ book.Title = "C#"; Console.WriteLine ("在方法中,变量的值是:{0}",book.Title); } //自定义图书类型 public class Book{ public string Title { get; set; } public string Author { get; set; } public decimal Price { get; set; } public DateTime PubDate { get; set; } }
引用类型参数:会影响方法外部变量的值
void Main() { var m = 3; Add(ref m); Console.WriteLine ("方法执行完毕后,变量的值是:{0}",m); } public void Add(ref int m){ m++; Console.WriteLine ("在方法中变量的值是:{0}", m); }
输出参数:会影响方法外部变量的值,通常在需要返回多值时使用
void Main() { string firstName; string lastName; Console.WriteLine ("请输入完整姓名,姓氏和名称用空格拆分:"); var fullName = Console.ReadLine(); ConvertName(fullName,out firstName, out lastName); Console.WriteLine ("{0},您的姓氏是:{1},名字是{1}",fullName, firstName, lastName); } public void ConvertName(string fullName,out string firstName,out string lastName){ firstName = fullName.Split(new[]{‘ ‘})[0]; lastName = fullName.Split(new[]{‘ ‘})[1]; Console.WriteLine ("在方法中通过空格拆分,获取姓氏和名字"); }
在C#中,也有很多类似使用输出参数的方法,比如TryPase()类型转换时的代码(主要用于防御式编码):
void Main() { Console.WriteLine ("请输入一个整数"); var input = Console.ReadLine(); int num; if (int.TryParse(input,out num)) { Console.WriteLine ("你输入的数字是:{0}",num); }else { Console.WriteLine ("输入格式不正确,请重新输入!"); } }
参数数组:可以输入数组类型,也可以直接忽略该参数,参数数组必须放置在方法最后一个参数,如
void Main() { var nums = Enumerable.Range(1,100).ToArray(); FilterNums(20,50).Dump(); FilterNums(20,24,nums).Dump(); } //返回介于最小和最大数字之间的 数字个数 public int[] FilterNums(int min,int max,params int[] array){ return array.Where (a => a>=min && a<=max).ToArray(); }
在.NET中,参数数组出现在ADO.NET中,如
void Main() { GetDataSet(@"data source=.;initial catalog=BookStoreDB;integrated security=true", "select * from Book where Title like @Title", new SqlParameter[]{new SqlParameter("@Title","%s%")}).Dump(); } public DataSet GetDataSet(string conStr,string cmdText,params SqlParameter[] values){ var cmd = new SqlCommand(cmdText,new SqlConnection(conStr)); cmd.Parameters.AddRange(values); var da = new SqlDataAdapter(cmd); var ds = new DataSet(); da.Fill(ds); return ds; }
新特性:可选参数、命名参数、可空类型参数等
可选参数(有默认值)必须在必选参数后面,如
void Main() { Show("@","!").Dump(); } public string Show(string prefix,string suffix,string message="Hello World"){ return prefix + message + suffix; }
命名参数(源自VB)允许调用时通过命名调整参数顺序,如
void Main() { Sub(numA:12,numB:3).Dump(); Sub(numB:3,numA:12).Dump(); } public int Sub(int numA,int numB){ return numA - numB; }
可空类型参数(主要应对与数据库中的可空字段)
void Main() { Sub(null,0).Dump(); Sub(12,3).Dump(); } public int Sub(int? numA,int? numB){ return numA??int.MaxValue - numB??0; }
方法重载(overload):根据参数列表(个数,类型,顺序)判断调用哪个同名方法
void Main() { Show(); Show(3,12); Show("ramm","derek"); } public void Show(){ Console.WriteLine ("没有参数的方法"); } public void Show(int m, int n){ Console.WriteLine ("两个参数中比较大的数字是:",m>n?m:n); } public void Show(string m, string n){ Console.WriteLine ("你好,{0} {1}",m,n); }
递归(自己调用自己):一种算法思想,注意结束条件,否则很容易形成死循环
void Main() { Show("通过递归,依次执行方法自身,直到满足条件时,退出方法"); } public void Show(string message){ if (message.Length>1) { Console.WriteLine (message); Show(message.Substring(1)); }else { Console.WriteLine (message); } }
执行效果如下
再比如,斐波那契额数列
void Main() { Foo(10).Dump(); } //斐波那契额数列:1,1,2,3,5,8,...... public long Foo(long index){ if (index==1 || index ==2) { return index; } return Foo(index-1) + Foo(index-2); }
或者控制台循环输入判断:
void Main() { Show(); } public void Show(){ var n = Convert.ToInt32(Console.ReadLine()); if (n!=0) { Console.WriteLine ("你输入的不是0,请再次输入"); Show(); }else{ Console.WriteLine ("好吧,程序可以退出了"); } }
最后,考虑阶乘累加如何通过递归实现? // 1! + 2! + 3! + ..... + n!