28.2 改进的参数传递
改进主要体现在如下几点:
❑可选和命名参数。所有参数都是可选的,如果参数的默认值是我们需要的,就可以忽略掉该参数。
❑ref修饰符可省略。不再需要ref修饰符,但仅限于COM Interop调用,其他情况下还是需要ref修饰符。
下面通过代码示例来阐述这两点改进。先从可选和命名参数开始,先来看一段代码示例,如代码清单28-6所示。这里有一个类:Book,它的构造函数有4个重载,以满足不同情况的需要,但随着需求的变化,可能已有的重载不能满足新的需求,这也意味着我们要提供更多的重载。
代码清单28-6 通过构造函数重载初始化Book对象
namespace ProgrammingCSharp4
{
class Book
{
//书名
public string Name{get;set;}
//作者
public string Author{get;set;}
//出版时间
public string Year{get;set;}
//出版社
public string Press{get;set;}
//构造函数
public Book(string name)
{
Name=name;
}
//重载
public Book(string name,string author)
{
Name=name;
Author=author;
}
//重载
public Book(string name,string author,string year)
{
Name=name;
Author=author;
Year=year;
}
//重载
public Book(string name,string author,string year,string press)
{
Name=name;
Author=author;
Year=year;
Press=press;
}
}
class Client
{
public static void Main()
{
Book book=null;
//分别使用4种构造函数实例化Book类
book=new Book(“C#4.0程序设计”);
book=new Book(“C#4.0程序设计”,“姜晓东”);
book=new Book(“C#4.0程序设计”,“姜晓东”,"2010");
book=new Book(“C#4.0程序设计”,“姜晓东”,"2010",“机械工业出版社”);
}
}
}
那么,有没有更好的办法来应对这种变化呢?可以使用“可选和命名参数”,只需要声明一个构造函数,它的参数列表包含上述4个参数;然后为每个参数提供默认值(这里为字符串提供空字符串作为默认值),也可以使用null作为默认值。之后,在使用这个类的时候,就非常灵活了,可以有选择性地对某参数赋值,只需要连带参数名一起提供参数值即可,而不需要赋值的参数,即使用默认值,如代码清单28-7所示。
代码清单28-7 使用命名参数改造后的代码
namespace ProgrammingCSharp4
{
class Book
{
//书名
public string Name{get;set;}
//作者
public string Author{get;set;}
//出版时间
public int Year{get;set;}
//出版社
public string Press{get;set;}
//使用命名参数,且有初始值
public Book(string name="",string author=“",int year=2010,
string press=“")
{
Name=name;
Author=author;
Year=year;
Press=press;
}
}
class Client
{
public static void Main()
{
Book book=null;
//因为每个参数都有了默认值,以下初始化仍然是合法的
book=new Book(“C#4.0权威指南”);
book=new Book(“C#4.0权威指南”,“姜晓东”);
book=new Book(“C#4.0权威指南”,“姜晓东”,2010);
book=new Book(“C#4.0权威指南”,“姜晓东”,2010,“机械工业出版社”);
//可根据参数名赋值,可根据需要选择初始化哪些参数
book=new Book(year:2010);
book=new Book(name:“C#4.0权威指南”,year:2010);
book=new Book(year:2010,press:“机械工业出版社”);
book=new Book(author:“姜晓东”,year:2010,press:“机械工业出版社”);
}
}
}
可见,可选和命名参数带来了非常大的灵活性,原来的多个构造函数重载都不需要了,代码少了,还更灵活了,何乐而不为呢?使用命名参数改进后的示意图如图28-8所示。
图 28-8 使用命名参数改进后的示意图
稍后将阐述如何在COM Interop调用中使用这一新特性。在C#4.0出现以前,访问COM组件往往是复杂和晦涩的,因为COM组件中的方法参数都是引用类型。如果方法有4个参数,即使不需要用到任何参数,换句话说,只需保持参数的默认值即可,也要为全部的4个参数赋值,只是给参数赋的是一种代表“没有值”的类型——Type.Missing,如代码清单28-8所示,即使不需要用到Add方法的任一个参数,也要使用Type.Missing为它们赋值。
代码清单28-8 没有使用到的参数也要赋值
object useDefaultValue=Type.Missing;
wordApp.Documents.Add(ref useDefaultValue
,ref useDefaultValue
,ref useDefaultValue
,ref useDefaultValue);
另外,请注意每个参数值前的ref修饰符,因为所有的参数类型都是引用类型,所以参数值必须按引用传递,因此要加上ref修饰符。这一点在C#4.0中也已经变得不必要,ref可以安全地省略。
完整的代码如代码清单28-9所示,请注意加粗部分代码。
代码清单28-9 在C#4.0之前访问COM对象的方法
using System;
using Excel=Microsoft.Office.Interop.Excel;
using Word=Microsoft.Office.Interop.Word;
using System.Collections.Generic;
namespace ProgrammingCSharp4
{
class COMInteropSample1
{
public static void Main()
{
var wordApp=new Word.Application();
wordApp.Visible=true;
object useDefaultValue=Type.Missing;
//为所有的参数赋值:useDefaultValue,并且是按引用传递参数值
wordApp.Documents.Add(ref useDefaultValue,ref useDefaultValue,ref
useDefaultValue,ref useDefaultValue);
object link=true;
object displayAsIcon=true;
//除了link和displayAsIcon这两个参数,其余的全部参数使用
//useDefaultValue作为参数值,并且是按引用传递参数值
wordApp.Selection.PasteSpecial(ref useDefaultValue,
ref link,
ref useDefaultValue,
ref displayAsIcon,
ref useDefaultValue,
ref useDefaultValue,
ref useDefaultValue);
}
}
}
接下来使用新的特性来改写那些复杂而晦涩的代码。在C#4.0中,所有参数都是可选的,再也不需要为全部的参数赋值了,只需根据自己的需要为特定的参数赋值,那些用不到的参数可以安全地被省略。另外,C#4.0会为传入的每一个参数值自动创建一个对象用以保存这些值,因此我们不需要每次声明一个新的对象,用以传送这些值了,ref修饰符也不需要了。此外,因为是根据参数名进行赋值,因此这些参数的顺序也可以和方法签名中的顺序不同,可以根据需要自行安排参数顺序。改写后的代码如代码清单28-10所示。
代码清单28-10 C#4.0中使用命名参数访问COM组件中的方法
using System;
using Excel=Microsoft.Office.Interop.Excel;
using Word=Microsoft.Office.Interop.Word;
using System.Collections.Generic;
namespace ProgrammingCSharp4
{
class COMInteropSample
{
public static void Main()
{
var wordApp=new Word.Application();
wordApp.Visible=true;
//用不到的参数不需要赋值,可以安全地省略
wordApp.Documents.Add();
//使用命名参数赋值,用不到的参数不需要赋值,ref修饰符也可省略
wordApp.Selection.PasteSpecial(Link:true,DisplayAsIcon:true);
}
}
}