APP下载

使用C# 7元组语言特性优化数据驱动软件研究

2017-09-29谢小魁

软件导刊 2017年9期
关键词:元组

摘 要:C#作为一种现代编程语言,广泛应用于通用业务系统开发。由于早期的C#语法限制,在数据密集型领域使用MatLab、Python、IDL(Interactive Data Language)等动态脚本语言更为合适。C# 7新增了简单灵活的轻量级元组类型。分析了元组的几个典型应用场景,并给出了具体示例,包括多变量初始化、多变量赋值、多变量互换、封装函数返回的多变量、作为中间层的数据容器、用于LINQ select表达式获得语义信息,以及利用析构(deconstructing)实现对象到元组的自动转换等。实践表明,元组适合数据驱动领域开发,提高了生产效率,达到了动态语言效果。对C# 7 tuple存在的缺陷,如deconstructing的非对称性、赋值的非传递性和可变性(mutable)等提出了改进意见,为利用开源编译器Roslyn优化C#特性提供了设计思路。

关键词:元组;动态脚本;语言设计;编译器Roslyn

DOI:10.11907/rjdk.171599

中图分类号:TP301 文献标识码:A 文章编号:1672-7800(2017)009-0007-03

Abstract:As a modern developing language, C# was widely used in general-purpose software development. But due to C# syntax limitation in the past, data-intensive application domain were dominated by dynamic script languages such as MatLab, Python, IDL(Interactive Data Language). C# 7 introduces tuple feature which is a simple and flexible data type.Typical application scenarios were analyzed and example details were presented, including initialization, assignment and swap of multiple variables, packing return values of a method, providing semantic information in LINQ select and transform object to tuple members automatically. The study showed that C# tuple is suitable for data-driven software development in order to improve developer productivity and achieves effects of dynamic languages. At last, the C# 7 defects in design, including asymmetry of deconstructing, non-transitivity of assignment and mutable were analyzed, and corresponding updating suggestion were presented, providing ideal for optimize C# 7 features in the future using open source compiler Roslyn.

Key Words:Tuple; Dynamic Script; Language Design; Compiler Roslyn

0 引言

C#作為一种通用的面向对象编程语言,提供了丰富的类型系统,使得编译器可进行强类型语法检查,能较好保证代码的安全性,在用户界面、数据库、网络、多媒体和地理信息系统等通用软件开发领域有着广泛应用。但由于语法限制,过去在数据驱动和交互脚本领域,仅仅是提取数据,开发者需要编写大量额外的类或结构体,掩盖了开发者的设计意图,降低了开发效率;同时C#的类实例(class instance)是引用类型(reference),会带来较大性能损失。

C# 7引入了tuple(元组)类型,由编译器和IDE(Integrated Development Environment)提供上下文智能感知和强类型检查,在Visual Studio 2017中可以使用。元组是一种比类和结构体更简单灵活的轻量级(lightweight)值类型(ValueType),完全不同于以前.NET平台的Tuple泛型API(Application Programming Interface)。为此,本文探讨该元组特性在数据驱动领域的开发及应用。

1 应用场景和示例

1.1 多变量同时初始化

若不利用元组语言特性,两个变量初始化一般做法为:

string name = "XiangRikui";

int age = 10;

利用元组语言特性,两个变量初始化可写成下面的第1句,第2句和第3句具有同样效果,只是利用了C#[1-8]的隐形类型(implicit type),由编译器的类型推断(type inference)功能确定真正的强类型(strong type)。

(string name, int age) = ("XiangRikui", 10);

(var name, var age) = ("XiangRikui", 10);endprint

var (name, age) = ("XiangRikui", 10);

1.2 多变量同时赋值

int age;

string name;

(name, age) = ("XiangRikui", 1);

利用元组的多变量同时初始化和同时赋值功能,计算斐波那契数列,可达到同动态语言Python相同的精简[9]:

int num = 100;

var (a, b) = (0, 1);

var fib = new List(); // containing Fibonacci series up to n

while (b < num)

{

fib.Add(b);

(a, b) = (b, a + b);

}

1.3 交换变量值

对于两个相同类型(例如字符串)的变量a和b,要交换值,传统的做法是引入第3个临时变量,而且要考虑临时变量的数据类型。

string temp = a;

a = b;

b = t;

利用元组,不用引入临时变量,可直接互换,这在数据排序里非常有用。利用这一特性,可同时交换任意数量的变量值。

(a, b) = (b, a);

1.4 封装函数返回的多个变量

在动态脚本语言如MatLab、Python和ENVI IDL(Interactive Data Language)中,都支持函数返回多个变量。但过去在C#中,一般需要编写新的类或结构体,而类或结构体意味着对数据操作,这掩盖了开发者意图,同时也带来了性能损失。利用元组,可以封装(pack)函数返回的多个变量。

例如,利用全站仪测量的水平距离和方位角,不用编写新的Point类型就可直接利用元组返回函数计算坐标增量(x, y)。

using static System.Math;

//from length and angle to compute coordinate increment (x,y)

public static (double x, double y) Rect(double length, double angle)

{

double x = length * Cos(angle);

double y = length * Sin(angle);

return (x, y);

}

//tuple function call demo

double len, alpha;

//…

var p = Rect(len, alpha);

1.5 作为中间层的数据容器

元组可存储来自文本文件、Excel、XMl和数据库中的数据,代替重量级的DataTable或自定义类型。

例如,激光扫描仪测量的彩色激光点云,同时具有三维坐标和颜色信息,利用元组返回测量值可表示如下:

(double x, double y, double z, int r, int g, int b) cloudPoint=GetCloudPoint();

1.6 获得语义信息

LINQ表达式中,select用命名元组(named tuple)表示可以获得的语义信息 :

IEnumerable<(int ID, string Title)> currentItems =

from item in AllItems

select (item.ID, item.Title);

如果不利用命名元組,则要么生成匿名对象,要么需要处理Item1、Item2这样的非语义名称。

1.7 实现对象到元组的自动转换

在类中定义Deconstruct函数,把组成该类型的字段用out参数表示:

public class Point

{

public Point(double x, double y)

{

this.X = x;

this.Y = y;

}

public double X { get; }

public double Y { get; }

//Deconstruct corresponding to tuple element unpack

public void Deconstruct(out double x, out double y)

{

x = this.X;

y = this.Y;

}

public static Point Construct((double x, double y) tuple)

{

Point result = new Point(tuple.x, tuple.y);

return result;

}

}

利用析构(Deconstruction)功能,编译器可拆封用户自定义类型字段,提取字段到相应的元组成员,实现对象到tuple(字段)的自动转换,tuple字段与Construct函数中的out变量一一对应:endprint

var p = new Point(3.14, 6.28);

(double X, double Y) = p;

2 C# 7 tuple存在的缺陷及设计建议

2.1 Deconstruction设计的非对称性

C# 7支持对象到元组(字段)的自动转换,但不支持tuple到对象的自动转换。在类中定义Deconstruct函数后,可以利用tuple自动提取相应字段,轻松实现对象到tuple(成员字段)的转换。但即使类实现了Construct函数,C#却没有提供tuple(成员字段)到对象的自动转换功能:

Point p = Point.Construct((3.14, 6.28));//right

// Cannot implicitly convert type '(double, double)' to 'Program.Point'

// Point p = (3.14, 6.28);//error

建议:增加Construct支持Deconstruction。

从语法设计的角度,增加对Construct的支持,由编译器进行类型检查,并自动调用Construct函数实现从元组到对象的自动转换:

Var tuple = (3.14, 6.28);

ClassName p = tuple; //设计建议,由编译器自动调用对应类的ClassName.Construct

ClassName p = ClassName.Construct(tuple);//right

2.2 tuple与Deconstruction赋值的非传递性

当赋值操作符右边为元组字面常量(tuple literal)时,左边可以命名元组变量(named tuple),或者拆封(unpack)元组成员。但当赋值操作符右边为对象时,左边只能为拆封元组成员形式,不能为元组变量:

/* 1 */ (double X, double Y) tuple = (3.14, 6.28); // tuple literal, right

/* 2 */ (double X, double Y) = (3.14, 6.28); // tuple literal, right

/* 3 */ var p = new Point(3.14, 6.28); //class object

/* 4 */ (double X, double Y) = p; // class object, right

//Cannot implicitly convert type 'Program.Point' to '(double x, double y)

/* 5 */ (double X, double Y) tuple = p; // error

建议:当赋值操作符右边为对象实例时,左边支持命名数组。

2.3 元组可变类型

元组是值类型(ValueType),属于可变类型(mutable)。在C#中,ValueType被认为是可变类型。在并行计算、多线程计算中,可变类型不是安全的类型。

建议:增加不可变元组(immutable tuple)

对所有值类型增加关键字immutable,以适用并行计算和多线程计算:

immutable var (X, Y) tuple = (3.14, 6.28);

3 结语

利用tuple打包(pack)数据,可以获得类似Python、MatLab和ENVI IDL(Interactive Data Language)等动态语言效果。但目前C#元组存在一些小的设计缺陷,通过修改C#开源编译器Roslyn[10],可以设计新特性。加上C#的动态特性(dynamic)、并行计算(parallel)、异步计算(async)、函数式编程(functional)和跨平台(.NET Core)等新特性,使这一通用语言在数据驱动、甚至在数值计算密集型领域得到更广泛的应用。

参考文献:

[1] 沈宫新.基于C#的Windows窗体端口扫描程序分析[J].软件导刊,2017(1):38-40.

[2] 严真卿,杨增汪.基于C#的即时通讯工具开发[J].电子技术与软件工程,2014(14):95-138.

[3] 周虎.基于C#的Excel数据批量导入SqlServer的方法研究与实现[J].软件工程师,2014(12):54-56.

[4] 王舜.基于C#的C/S和B/S职场发展分析[J].计算机光盘软件与应用,2014(2):135-136.

[5] 谢小魁,陈青海,周清,等.基于C#.NET和开放数据的大比例尺高精度数字高程模型提取研究[J].测绘与空间地理信息,2016(9):13-15.

[6] 谢小魁,方武生,田良辉,等.基于EXCEL VSTO的测量导线计算教学系统设计[J].矿山测量,2016(4):95-122.

[7] MICROSOFT. What's new in C# 7[EB/OL]. https://docs.microsoft.com/en-us/dotnet/articles/csharp/whats-new/csharp-7.

[8] MICROSOFT. C# tuple types[EB/OL]. https://docs.microsoft.com/en-us/dotnet/articles/csharp/tuples.

[9] PYTHON SOFTWARE FOUNDATION. Defining functions[EB/OL]. https://docs.python.org/3/tutorial/controlflow.html#defining-functions.

[10] MICROSOFT. .NET compiler platform ("Roslyn")[EB/OL]. https://github.com/dotnet/Roslyn.

(责任编輯:杜能钢)endprint

猜你喜欢

元组
基于词嵌入的元组级数据溯源方法
Python核心语法
针对隐藏Web数据库的Skyline查询方法研究*
QJoin:质量驱动的乱序数据流连接处理技术*
一种基于时间戳的简单表缩减算法∗
海量数据上有效的top-kSkyline查询算法*
Mixly开源项目设计28:秒懂74HC595移位寄存器(三)
不确定数据的有效查询处理评估技术研究
基于减少检索的负表约束优化算法
基于差异化聚类的分级隐私保护数据发布方法