近日M#的消息令江湖再次起了波澜。大家知道,.NET已经进入了瓶颈期。这个消息又让偶有所期待,趁此机会发表一下个人的展望,对C#或者其继任者,不管是M#还是X#。
一、语法特性
1. using引入类型
using关键字在.NET中有两个意义,这里是指引入命名空间。要是支持using类型会带来很大方便,比如:
using System.Runtime.InteropServices.Marshall; //可以直接调用Marshall类的静态方法和属性 using List<string>= StringList; //写起来不用再打<>
2. 变量命名空间
有时候,处理业务很复杂,即使在一个不能再分割的处理单元中也有许多变量,命名和组织成了一个头疼的问题,将这些封装成类又不值得。如果可以在声明变量或字段时定义命名空间,这个烦恼就能解决:
class ProductUtil{ namespace Price { float old; float New; float standard; } float GetPrice(){ float Price.discount; //声明折扣 //计算价格... return Price.New; } }
命名空间可以在方法内,也可以在类型内。声明命空间后,可以单独声明一个变量。
3. 用out声明变量.
有点经验的开发者会经常用Try-Parse避免产生异常,一般TryParse、TryGet这类方法都把结果传给用out声明的参数中,我总是在想,为什么这个参数要先声明呢?
int value; //这个声明完全可以去掉,看起来不爽 if (int.TryParse(s, out value)) { Console.WriteLine(value); }
4. 省略属性字段
C# 3.0推出了简化属性的方式:public string Text { get;set; }。但还不彻底,对于get set内部有一定处理逻辑的,无法省略字段。如果可以用一个关键字代替字段,将使代码更整洁。
我觉得用default在属性内表示字段不错,如果在内部其他方法中要直接访问此字段(应该很谨慎地),可以用default(this.Text),好像有点麻烦,但这种情况很少,这样用会更醒目。
5. 能做就做
要说最烦的,还是这种判断代码
if(list != null && list.Count > 0){ return list.Last(); }
很多时候,根本无需处理list为null或空的情况,如果上面的代码能可以写成这样,必然很受欢迎:
return list.Last()??;
从此以后,我们可以唱:兄弟你大胆地往下写哟~~,别怕异常~~
6. 带参构造函数约束
泛型约束只能声明无参构造函数,这种限制没任何道理。应可以像下面这样。
public class MyClass<T> where T : class, new(int) { ... }
或者不限制构造函数的参数:
public class MyClass<T> where T : class, new { ... }
如果调用了不兼容的构造函数,IDE完全可以在编译时检查出来。
7. case可跟表达式
刚学编程接触switch case时,就很不解case后面只能跟常量的限制。这其实应该是个bug,包括其他语言。
8. 扩展属性
当时扩展方法推出的时候,为什么没有扩展属性呢?我知道肯定有某种理由,但这个真可以有。
9. 绑定运算符
对于两个类实例 A=B 将使AB指向同一个引用,若随后B引用变化了,对A没有影响。许多初学者搞不清=运算符的作用,以为B变化,A也随之变化。
其实这种特性是C#一直都欠缺的,如果我们可以让 A==B 实现A和B的绑定(左边可以是表达式),那许多工作就轻松了。
要考虑GC的问题,如果A未被回收,B也不能被回收。
10. 项目引用Native dll
如今要调用C/C++的程序集,必须查好参数,转化类型,再像这种方式声明:
[DllImport("User32.dll", CharSet = CharSet.Unicode)] public static extern int MessageBox(int h, string m, string c, int type);
为什么不能像引用.NET dll那样引用C/C++写的dll呢,或者像Service Reference那样,增加一个Native Reference的目录,自动生成代理类。
11. Fluent语法
利用语法糖,希望能够大幅减少关键字使用,比如对于(int)value, value as string都可以用ToSomeType()表示。若调用构造函数,可以省略new关键字。
在使用第一条using声明调用静态方法的类后,可以将静态方法作为任一参数的扩展方法。
using String; static void SortDates(string dates) { var lstDates = dates.Split(',').Select(d=>d.ToDateTime()); //ToDateTime()相当于(DateTime) var endDate = DateTime(lstDates.Max().Year, 12 ,31); //省略new dates = lstDates.OrderBy(d=>d).Join(","); //String.Join方法变扩展方法 }
12. 全局性配置
这其实是由Visual Studio实现的功能。如果实现扩展属性,直接调用类静态方法,会导致自动提示里拥挤不堪,让便利性打折扣。应该可以在项目属性里配置,导入一个配置模板,默认隐藏掉那些不常用的成员,比如所有类型都甩不掉的Equals/ReferenceEquals/GetHashCode。这个模板还可以用using为类型起别名,就不必在每个文件中一一声明了。
这样C#关键字会大大减少,还可以定制更加符合个人或团队习惯的命名。
这几天又想到两个特性,补充一下(1-11)
13. 真正的”转到定义“
F12转到方法的定义,多数时候很方便,但有两个很不方便的问题。一个是方法被子类override时,F12会转到父类的方法,这在开发时也就罢了,调试时VS完全知道真正调用的方法是否被覆写过,应该转到真正调用的方法上去。还有一个是客户端调用WCF或Service代理类的方法,F12不应转到代理类中,应该转到项目中WCF或Service方法的定义,除非是第三方的服务。查找引用时,也应该将Server/Client端代码一并包括。
14. 程序集自定义引用
引用一个程序集时,应该像安装程序一样,可以定义引用程序集哪些模块,这样就可以大大减小安装包和程序的体积。别说这是异想天开,CIL本身就支持Module的概念,只是现在编译器偷懒没有实现而已。
二、动态支持
C#的反射虽然很完善了,但性能还是差,并且相当不友好。如果要运行时修改代码,即使用最先进的Lamda Expression,还有第三方框架MonoCecil都很麻烦,更别说用Emit和Code Generator。所以C#基本上还是一门静态语言,这样满足诸如AOP这种高扩展性开发需求,仍然十分吃力。
我们早对一个基于项目的元数据框架望眼欲穿了,彻底解放被元数据和动态代码束缚已久的生产力。
首先,要增强T4模板,使之更方便地引用自身和第三方程序集。实现像Razor那种语法支持应该不过分吧。使用好T4模板,可以大大提高代码重用性(对AVL树和红黑树的个人理解),还有避免元数据操作(性能相差7千倍的ToString方法)。
对于一个项目,应该提供一个编译选项,可以自动生成强类型的元数据程序集。比如MVC中,可以这样用:
using MySite.Metadata; //引入自动生成的元数据程序集 namespace MySite.Controllers { public class HomeController : Controller { public ActionResult Home() { if (!User.Identity.IsAuthenticated) return Views.Login; //Views自动生成,取代 View("Login") return View(); } } }
假设有一个Person类,有Name/Sex/Birthday三个属性,我们希望这样运行时加一个Age属性:
MyProject.Metadata.Class.Person.CreateProperty("Age", p=> (DateTime.Now - p.Birthday).Year);
调用这些动态属性时,只要用dynamc特性即可(当前这个特性应用很有限),最好能为dynamic类型提供一个类似JQuery的attr函数。 如果能实现JS的eval函数的功能就更好了,那样C#代码也许会变得我都不认识了。
三、设计面向
编程语言发展已经超过半个世纪了,先是面向变量的汇编语言,高级语言出现后,从Pascal/C语言面向过程和变量,到C++以后的面向对象。 C#和Java只是语言特性上有大幅改进,设计思想并没有飞跃。
C#作为最为先进的编程语言,反映了当前语言发展的瓶颈。要有所突破,必须要有新的设计思想,把面向粒度提高到新的层次。
个人分析后认为,未来C#或C#的后继者,会向三个方向发展:
1. 面向集合
未来编程语言遇到的业务逻辑将更复杂,对集合处理是业务逻辑的核心内容。LINQ使C#走在业界的前列,然而还有许多问题。
由于历史原因,集合类型太多太乱。支持泛型是必须,我们需要根据可变性、排序性、Hash特性、并发要求等,使用一致的高性能集合类型。这些集合类能够灵活转化,智能地处理扩容、复制等底层操作,且没有LINQ那样无法跨程序域传递的限制。这需要框架和CLR双重支持。
2. 面向并发
这个是很自然的方向,除非出现光、生物、量子计算技术的飞跃,不然半导体电路处理器单核极限愈来愈难突破,多核趋势愈演愈烈。未来的编程语言,并发支持必须融入其底层。
还是拿Node.js来说,已经初具此特性,其对IO的访问全部非阻塞的,是从底层支持的异步操作。
对于C#来说,就不只是框架上修修补补,而是CLR的全面支持。async和await出现是个很好的苗头,期待看到更多这样的发展。
3. 面向任务
Node.js它通过事件轮询(event loop)来实现并行操作,这只能处理最简单地多任务同步。要实现真正的并发语言,并满足日益复杂的业务逻辑处理,必须对并发的单元-任务进行有力的支持。
.NET对任务有了System.Threading.Tasks下一系列类的支持,但这只是开始。我们需要动态地创建、分解、修改、取消任务,需要方便地获取和控制任务的状态,管理超时和资源,统计任务效率,处理异常。
前面的路还很远,很长,也一定很精彩。以后我还会继续研究这些方向。
四、平台支持
M#既然能开发操作系统,那定是能编译成Native Code。其实C#本来早就可以有这个功能了,把编译器和NGEN结合一下就行了。但我倒觉得即使有也不没有什么激动人心的,毕竟操作系统才几个?接触的人屈指可数。何况微软一个新操作系统未来能赢得多少市场也未敢期望太高。重要一百倍的平台支持,我认为是各种浏览器IE/FF/Chrome,或者三大浏览器引擎。
其次,至少微软自家的产品IE应该支持,还有SQL Server/Office这些重量级产品,不支持实在说不过去。
我曾经以为Javascript会在数年被淘汰,然而现在都开始逆袭Server端了。如果C#开发的程序能直接在各种浏览器上运行,C#才真正迎来了春天。