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.2 改进的参数传递 - 图1

图 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);

}

}

}