private static (int Max, int Min) Range(IEnumerable numbers)
{
int min = int.MaxValue;
int max = int.MinValue;
foreach(var n in numbers)
{
min = (n < min) ? n : min;
max = (n > max) ? n : max;
}
return (max, min);
}
这里使用元组带来了如下优势:
省事。不需要为返回类型定义 class 或 struct
不需要创建新的类型
增强的语言不再需要调用 Create(T1) 方法
方法申明中为返回元组数据的字段提供了名称。调用这个方法的时候,返回的元组会有 Max 和 Min 两个字段:
你也可以为 .NET 中任意类类型提供类似的解构功能,方法是为类提供一个 Deconstruct 成员方法(译者注:称为解构方法,注意与析构方法区分)。Deconstruct 方法提供一组 out 参数,对应于你想解构出来的每一个属性。下面的 Point 类提供了一个解构方法用于提取 X 和 Y 坐标:
public class Point
{
public Point(double x, double y)
{
this.X = x;
this.Y = y;
}
public double X { get; }
public double Y { get; }
public void Deconstruct(out double x, out double y)
{
x = this.X;
y = this.Y;
}
}
现在可以通过将 Point 对象赋值到元组来提取各个字段:
var p = new Point(3.14, 2.71);
(double X, double Y) = p;
public static int DiceSum3(IEnumerable values)
{
var sum = 0;
foreach (var item in values)
{
switch (item)
{
case int val:
sum += val;
break;
case IEnumerable subList:
sum += DiceSum3(subList);
break;
}
}
return sum;
}
匹配表达式的语法与 is 表达式略有不同,是在 case 表达式的开始位置申明类型和变量。
匹配表达式也支持常量,这在遇到简单的条件判断时会节约不少时间:
public static int DiceSum4(IEnumerable values)
{
var sum = 0;
foreach (var item in values)
{
switch (item)
{
case 0:
break;
case int val:
sum += val;
break;
case IEnumerable subList when subList.Any():
sum += DiceSum4(subList);
break;
case IEnumerable subList:
break;
case null:
break;
default:
throw new InvalidOperationException("unknown item type");
}
}
return sum;
}
上面的代码添加了 0 作为 int 的特殊情况,null 则是另一个特殊情况,代表没有输入。这演示了 switch 模式表达式中一项重要特性:需要注意 case 表达式的顺序。0 这个条件必须出现在其它 int 条件之前。要不然,int 条件会先匹配到,即使值为 0。如果你搞错了匹配表达式的顺序,一个本应该后匹配到的条件被提前处理了,编译器会标记出来并产生一个错误。
public struct PercentileDie
{
public int Value { get; }
public int Multiplier { get; }
public PercentileDie(int multiplier, int value)
{
this.Value = value;
this.Multiplier = multiplier;
}
}
然后,添加 case 匹配表达式来处理这种新类型:
public static int DiceSum5(IEnumerable values)
{
var sum = 0;
foreach (var item in values)
{
switch (item)
{
case 0:
break;
case int val:
sum += val;
break;
case PercentileDie die:
sum += die.Multiplier * die.Value;
break;
case IEnumerable subList when subList.Any():
sum += DiceSum5(subList);
break;
case IEnumerable subList:
break;
case null:
break;
default:
throw new InvalidOperationException("unknown item type");
}
}
return sum;
}
public static (int i, int j) Find(int[,] matrix, Func predicate)
{
for (int i = 0; i < matrix.GetLength(0); i++)
for (int j = 0; j < matrix.GetLength(1); j++)
if (predicate(matrix[i, j]))
return (i, j);
return (-1, -1); // Not found
}
这个代码中有很多问题。首先,它是一返回元组的公共方法,虽然从语法上来说没有问题,但对于公共 API 来说最好是使用用户定义的类型(class 或 struct)。
其次,这个方法返回了矩阵中某项的索引,调用者可以通过这对索引引用矩阵中的元素,并修改其值。
var indices = MatrixSearch.Find(matrix, (val) => val == 42);
Console.WriteLine(indices);
matrix[indices.i, indices.j] = 24;
public static ref int Find3(int[,] matrix, Func predicate)
{
for (int i = 0; i < matrix.GetLength(0); i++)
for (int j = 0; j < matrix.GetLength(1); j++)
if (predicate(matrix[i, j]))
return ref matrix[i, j];
throw new InvalidOperationException("Not found");
}
现在这个方法返回矩阵某个整数值的引用,你需要在调用的位置对它进行修改。var 申明知道 varItem 现在是 int 而不是元组:
var valItem = MatrixSearch.Find3(matrix, (val) => val == 42);
Console.WriteLine(valItem);
valItem = 24;
Console.WriteLine(matrix[4, 2]);
public static IEnumerable AlphabetSubset(char start, char end)
{
if ((start < 'a') || (start > 'z'))
throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
if ((end < 'a') || (end > 'z'))
throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");
if (end <= start)
throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");
for (var c = start; c < end; c++)
yield return c;
}
检查下面的代码对迭代方法地错误调用:
var resultSet = Iterator.AlphabetSubset('f', 'a');
Console.WriteLine("iterator created");
foreach (var thing in resultSet)
Console.Write($"{thing}, ");
public static IEnumerable AlphabetSubset2(char start, char end)
{
if ((start < 'a') || (start > 'z'))
throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
if ((end < 'a') || (end > 'z'))
throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");
if (end <= start)
throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");
return alphabetSubsetImplementation(start, end);
}
private static IEnumerable alphabetSubsetImplementation(char start, char end)
{
for (var c = start; c < end; c++)
yield return c;
}
如果把 alphabetSubsetImplementation 定义为公共 API 方法中的一个局部函数,就清楚多了:
public static IEnumerable AlphabetSubset3(char start, char end)
{
if ((start < 'a') || (start > 'z'))
throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
if ((end < 'a') || (end > 'z'))
throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");
if (end <= start)
throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");
return alphabetSubsetImplementation();
IEnumerable alphabetSubsetImplementation()
{
for (var c = start; c < end; c++)
yield return c;
}
}
public Task PerformLongRunningWork(string address, int index, string name)
{
if (string.IsNullOrWhiteSpace(address))
throw new ArgumentException(message: "An address is required", paramName: nameof(address));
if (index < 0)
throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));
return longRunningWorkImplementation();
async Task longRunningWorkImplementation()
{
var interimResult = await FirstWork(address);
var secondResult = await SecondStep(index, name);
return $"The results are {interimResult} and {secondResult}. Enjoy.";
}
}
注:某些设计使用 Lambda 表达式 来作为局部函数。有兴趣的朋友可以去看看它们之间的区别
更多成员支持表达式作为函数体
C# 6 对成员函数和只读属性引入了使用表达式作为函数体的成员。C# 7 扩展了允许使用这一特性的成员。在 C# 7 中,可以对构造方法、析构方法、属性的 get 和 set 访问器以及索引使用这一特性,下面是示例:
// Expression-bodied constructor
public ExpressionMembersExample(string label) => this.Label = label;
// Expression-bodied finalizer
~ExpressionMembersExample() => Console.Error.WriteLine("Finalized!");
private string label;
// Expression-bodied get / set accessors.
public string Label
{
get => label;
set => this.label = value ?? "Default label";
}
public string Name
{
get => name;
set => name = value ??
throw new ArgumentNullException(paramName: nameof(value), message: "New name must not be null");
}
这个特性也允许在初始化表达式中使用 throw 表达式:
private ConfigResource loadedConfig = LoadConfigResourceOrDefault() ??
throw new InvalidOperationException("Could not load config");
而在以前,这些初始化过程都需要在构造方法中进行,在函数体中使用 throw 语句:
public ApplicationOptions()
{
loadedConfig = LoadConfigResourceOrDefault();
if (loadedConfig == null)
throw new InvalidOperationException("Could not load config");
}
public const int One = 0b0001;
public const int Two = 0b0010;
public const int Four = 0b0100;
public const int Eight = 0b1000;
0b 开始的常量表示它们被写作二进制数。
二进制数可能会很长,所以引入 _ 作为数字分隔符:
public const int Sixteen = 0b0001_0000;
public const int ThirtyTwo = 0b0010_0000;
public const int SixtyFour = 0b0100_0000;
public const int OneHundredTwentyEight = 0b1000_0000;
数字分隔符可以出现在常量中任何地方。对于 10 进制数来说,它们常用作千分位分隔符:
public const long BillionsAndBillions = 100_000_000_000;
数字分隔符也可用于 decimal、float 和 double:
public const double AvogadroConstant = 6.022_140_857_747_474e23;
public const decimal GoldenRatio = 1.618_033_988_749_894_848_204_586_834_365_638_117_720_309_179M;
1. If语句作为表达式
val properties = if (jobIdToActiveJob.contains(jobId)) {
jobIdToActiveJob(stage.jobId).properties
} else {
// this stage will be assigned to "default" po
基础测试题
卷面上不能出现任何的涂写文字,所有的答案要求写在答题纸上,考卷不得带走。
选择题
1、 What will happen when you attempt to compile and run the following code? (3)
public class Static {
static {
int x = 5; // 在static内有效
}
st
表里的一行对于一个数据块太大的情况有二种(一行在一个数据块里放不下)
第一种情况:
INSERT的时候,INSERT时候行的大小就超一个块的大小。Oracle把这行的数据存储在一连串的数据块里(Oracle Stores the data for the row in a chain of data blocks),这种情况称为行链接(Row Chain),一般不可避免(除非使用更大的数据
Follow up for "Remove Duplicates":What if duplicates are allowed at most twice?
For example,Given sorted array nums = [1,1,1,2,2,3],
Your function should return length