本练习讨论方法形参的一些细节,以及它们在 C# 中的用法。
形参把信息从调用方法传递给被调用的方法。
形参的行为可以通过ref和out等修饰符来改变。
带有out修饰符的形参会把一个值从被调用的方法回传给调用方。这样的形参可以在未初始化的情况下传递给被调用的方法,而且在被调用的方法内部,无论怎样都会被当作进入时尚未初始化来处理。要理解形参修饰符的行为和规则,最容易的办法是通过示例(见下文)和编译器消息。
void Foo(out int val)
{
val = 42;
}
void Bar(int val)
{
val = 42;
}
int importantValue = 1729;
Foo(out importantValue);
var result = importantValue == 42;
// => true
importantValue = 1729;
Bar(importantValue);
result = importantValue == 1729;
// => true
out形参必须在被调用的方法内部被赋值。
带有ref修饰符的形参把值传入被调用的方法。方法返回时,调用方会在该形参中看到被调用的方法所做的任何更新。
void Foo(ref int val)
{
val *= 7;
}
int importantValue = 6;
Foo(ref importantValue);
return importantValue;
// => 42
ref形参必须是变量,因为被调用的方法会直接操作调用方所看到的那个形参。out和ref修饰符在被调用的方法签名中和调用处都必须写出。你在classes练习中启动的遥控车项目进展顺利(恭喜!),而且因为最近谈成了好几个赞助合作,预算里有了可以用于改进的资金。
其中一部分预算被用来提供遥测功能。
为了让赞助商满意,车上安装了一块面板,在车绕赛道行驶时显示赞助商名称。
你会注意到,引入这些花哨的新技术大幅缩短了汽车的电池续航。
实现SetSponsors(),接收一个或多个赞助商名称并把它们存储在汽车上。
注意,SetSponsors()方法的实参保证非空。
实现DisplaySponsor()来显示选中的赞助商。传入的第一个赞助商的sponsorNum为 0,第二个为 1,以此类推。
var car = RemoteControlCar.Buy();
car.SetSponsors("Exercism", "Walker Industries", "Acme Co.");
var sp2 = car.DisplaySponsor(sponsorNum: 1);
// => "Walker Industries"
实现RemoteControlCar.GetTelemetryData()方法。
GetTelemetryData()应通过out形参提供电池电量百分比和以米为单位的已行驶距离。
如果 serialNum 实参小于之前收到的值,GetTelemetryData()应返回false。(这里涉及多个遥测节点的问题。)在这种情况下,serialNum会被设为之前最高的serialNum,电池电量百分比和已行驶米数都会被设为-1。调用成功时,serialNum保持不变。
var car = RemoteControlCar.Buy();
car.Drive();
car.Drive();
int serialNum = 4;
car.GetTelemetryData(ref serialNum, out int batteryPercentage, out int distanceDrivenInMeters);
// => true, 4L, 80, 4
serialNum = 1;
car.GetTelemetryData(ref serialNum, out batteryPercentage, out distanceDrivenInMeters);
// => false, 4L, -1, -1
实现TelemetryClient.GetBatteryUsagePerMeter()方法。
它会调用RemoteControlCar.GetTelemetryData()。如果GetTelemetryData()返回false,或者汽车还没有行驶过,那么这个例程应返回“no data”。如果GetTelemetryData()返回true,则应返回如下形式的消息“usage-per-meter=BATTERY-USAGE-PER-METER”。其中的计算方式是(100 减去当前电池电量百分比)除以目前行驶的米数。
var car = RemoteControlCar.Buy();
car.Drive();
car.Drive();
var tc = new TelemetryClient(car);
int serialNum = 4;
tc.GetBatteryUsagePerMeter(serialNum);
// => "usage-per-meter=5"
serialNum = 1;
tc.GetBatteryUsagePerMeter(serialNum);
// => "no data"