由扩展方法引“.NET研究”申出的编程思维

  1. Helper大爆炸上海闵行企业网站设计与制作g>

  .NET Framework为我们提供了丰富的类库,但是这并不是万能地,在大部分的时间,我们都需要为我们的项目特殊定制我们的通用类库。

  常常,我们都可以构造一个类,类里封装一些方法。但是对于很多时候,我们并没有办法提取出这样一个类,举一个小例子,我们在很多时候,需要把url给保存到数据库里,作为一个唯一标识,但是我们知道url所占空间很大,如果用url来建立索引的话是非常耗费空间,而且影响效率的,那么我们最常用的办法就是把url做一个Hash来作为索引的替代品。

  这个时候,我们根本就没有办法说我们来怎么样提取一个类,然后在类里写这样一个方法,这个时候,我们通常就只能这样:


public static class HashHelper
{
public static string GetHashCode(string s)
{
//GetHashCode........
return String.Empty
}
}

  然后我们会这样使用:


public static void Main(string[] args)
{
string url = "www.fandongxi.com"
string sql = "insert into Test values('"+HashHelper.GetHashCode(url)+"')"
//执行SQL
}

  这里,只是一个例子,并不是说我们要这个样子拼接字符串。

  很快,肯定又会出现一个情况,说,我们要保存网页的内容,但是网页的内容直接存储到数据库里太大了,那么我们就需要对网页文本做一个Base64的压缩。

  那么,我们就又得继续写:


public上海网站建设e="color: #000000;"> static class Base64Helper
{
public static string GetBase64Text(string text)
{
//Base64........
return String.Empty
}
}

  接下来我们在使用的地方就又多出来一个Base64Helper。那么过几天,还会出现SHA1Helper , MD5Helper等等各种各样的Helper。

  渐渐地,我们会不会发现,Helper的数量已经让我们难以忍受了呢?

  2. 扩展方法的提出

  接下来的事情,我们都知道了,在.NET Framework 3.5中,也就是在C#3.0中,引入了扩展方法这个概念。

  那就让我们扩展方法来解决上面的难题。

  各位现在一定知道,无论是做UrlHashCode,还是Base64压缩,还是SHA1加密,还是MD5加密,这些都是针对字符串,或者说是一段文本的处理,那么很自然地,我们就需要把这些全部写入String类的扩展方法中。


public static class ExtensionClass
{
public static string GetHashCode(this string s)
{
//........
}
public static string GetBase64Text(上海企业网站制作style="color: #0000ff;">this string text)
{
//.......
}
}

public static void Main(string[] args)
{
string url = "www.fandongxi.com"
string sql = "insert into Test values('"+url.GetHashCode()+"')"
//执行SQL
}

  在这里,我不想剖析去读扩展方法的实现本质,这里我们只谈编程思维和扩展方法所带来的意义。

  3. 扩展方法让C#更加面向对象

  从面向对象的角度来看,世间万物皆为对象,所有属性,所有方法都是属于某一个对象的,那么再从这个角度看开去,本就不应该存在静态类,也不应该存在静态方法,所谓的静态,不过是面向对象语言对并不成熟的语法实现的一种屈从罢了。

  我们要求Base64加密后的文本,其实是文本调用自身的一个方法,之所以我们在之前的方法中需要一个Base64Helper,而不能这样子"http://www.fandongxi.com%22.replace(%22com%22,%22cn/")直接调用,只是因为.NET Framework无法预计到我们所有的业务场景,所以把只能把最通用的方法封装到已有的类库中。

  4. 从扩展方法向外谈一些

  让我们从扩展方法逐渐地向外围来探讨一些关于编码规范,以及一些代码优雅的问题。我们先不妨假设我们并不存在“+”运算符,或者说,我们禁止在程序中使用+运算符,那么也就是说,我们需要对“+”这个操作来做一个简单的封装,那么我们常规意义上会怎么做?


public int Add(int a,int b)
{
return a+b;
}

public static void Main(string[] args)
{
int result = Add(3,4)
Console.WriteLine(result)
}

  让我们来看这个函数,我们顺着代码的意思向下读,加,3,4。这明显是不符合我们常规的数学思维的,如果用了扩展方法之后,我们一定是应该这样来写。


public 上海闵行企业网站制作="color: #0000ff;">static class Extension
{
public static int Add(this int a,int b)
{
return a+b;
}
}

public static void Main(string[] args)
{
int a = 3;
a.Add(b)
}

  可是这个"."运算符看上去还是那么有点别扭…..没办法,至少这样读上去让我们的代码顺畅了很多不是么?像写文章,说话一样写代码一直是我们程序员追求的最高境界,就像这样的代码总是好的。

  Good:people.eat(food)

  而不是Bad:Eat(people,food)

  对把!

  5. 前缀,中缀和后缀表达式

  说到这,就不得不谈谈前缀,中缀和后缀表达式了。

  学过数据结构的朋友们,一定都记得在数据结构中,有一道经典的习题,就是利用“栈”来实现前缀,中缀和后缀表达式的转换。在考试题中也经常会出现这样的习题。那现在让我们来复习一下,什么是前缀,中缀和后缀表达式。

前缀表达式就是不含括号的算术表达式,而且它是将运算符写在前面,操作数写在后面的表达式,也称为“波兰式”。

  大名鼎鼎的Lisp就是前缀表达式的典型,让我们看一个最简单的小例子,还是那个经典的斐波那契数列:


(define (fib n)
(fib-iter 1 0 n))

(defile (fib-iter a b count)
(if (= count 0)
b
(fib-iter (+ a b) a (- count 1))))

  每次写Lisp的时候,都会被密密麻麻的括号所吓到,可是真的没什么太好的解决方案呢!

  中缀表达式就很简单了,和我们常规所涉及到的代码是一样的,后缀也是一个道理,在此就不再一一赘述。鉴于后缀的应用不是很大,在此我们也只谈谈前缀和中缀的意义。

  那么我们想想,为什么Lisp要采用这么蹩脚的前缀表达式语法呢?

  记得在大二第一次学习C语言的时候,老师让我们写一个简单的计算器,当时每个同学都写出了+,-,*,/的操作,但是在当时大多数的我们都没有办法写出更为常用的混合运算,以及()的操作,当时只有班上某鹤立鸡群的哥们写出了让我们当时完全无法看懂的代码。再直到大三学习数据结构,再反过来想他当时的代码,才恍然大悟。

  废话说了一堆,那么其实前缀表达式最大的意义就是他更贴近计算机的思维,他只需要两种操作就能完成运算,就是入栈和出栈。让我们来看一个简单的小例子

  3+(1-4),首先这是一个中缀表达式,把他转换为前缀表达式就是+3 – 1 4,计算机会从右向左来扫描这个表达式,4入栈,1入栈,然后遇到 - ,1和4出栈,并且完成运算,(-3)入栈,3进栈,+入栈,(-3)和3出栈,完成运算。

  也就是说,其实在计算机完成我们所编写的数学操作时,其实往往都是把我们的中缀表达式首先转换为前缀表达式,然后完成计算,而Lisp采用前缀表达式,则是省去了这一个步骤,从而提高解释器的效率。

  那我们就来总结下前缀和中缀表达式的意义。

  前缀表达式更加贴近计算机思维,方便计算。而中缀表达式更加贴近数学思维,容易被我们所理解。

  那回顾下,我们之前写Add的代码,如果说我们去掉.运算符,而且方法不加括号,是否采用扩展方法,把C#的语法和Lisp的语法相结合,其实就成了这样的形式。


public int Add(int a,int b)
{
return a+b;
}
public static void Main(string[] args上海企业网站设计与制作)
{
(set! result (Add a b))
}
public static class Extension
{
public static int Add(this int a,int b)
{
return a+b;
}
}
public static void Main(string[] args)
{
(set! result (a Add b))
}

  还是后者更贴近我们的自然思维一些。

  .NET Framework很强大,给我们提供了扩展方法这个概念,那么如果没有了扩展方法,其他语言给出了怎么样的解决方案呢?

  那让我们来看看Haskell给出的方案。

  5. 看看Haskell的方法

  Haskell是一门函数式的语言,在FP大行其道的今天,Haskell这门久居深宫的语言也渐渐地浮出了水面。

  废话不多说,我们只来看看Haskell是如何在没有扩展方法的情况下来解决语法和自然思维不相协调的问题的。

  让我们先来编写一个简单的Haskell函数。

  add x y = x + y

  代码很简单,没什么值得多说,让我们来看看Haskell怎么调用。

  这是我们传统的调用方式,可是Haskell为了更贴近我们的自然思维,为参数个数数量为2的方法提供了这样一个便捷的调用:

  这就是Haskell为我们提供的“中缀表达式”的解决方案。

  扩展方法很好,但是当我们的语言中没有扩展方法的时候,Haskell给我们提供了一个优秀的典范。

  6. 语言和类库

  说到这,我就想顺便谈谈关于语言扩展和类库扩展的问题。

  在《Masterminds of Programmming》一书中,Python语言之父Guido在接受采访时,谈到PEP(Python增强处理)时,顺便说到了关于在编写编程语言时,如何来根据用户的意见来处理语言实现的问题。

  他谈到:

如果某个用户提出一个新特性,它几乎不会成功。因为用户对实现没有全面的理解,他几乎不可能提出一个合理的新特性。

  那么在我看来什么是用户?用户就是使用这门语言来完成工作任务的人,他们往往需要的都是增加一个新功能,换句话说,他们需要的仅仅是一个方法而已。

  那么什么是增加语言特性,什么是增加类库方法,Guido也给出了比较合理的解释。

如果某个特性对于Web来说确实很棒,那么,对于加到语言中来说,就未必是优秀的特性了。如果它确实利于编写更短的函数,或者是有利于编写可维护更强的类,把它添加到语言中可能就是一件好事。

  其实Guido的意思很简单,是否增加到语言中,关于在于这个特性是否是领域相关的,如果是领域相关的,也许它需要做的仅仅是扩展类库,无论是增加Python的类库,还是用C去扩展Python API,总之无需对语言做出改变。

  那么对于C#来说,什么是类库的修改,什么是语言的修改,在我看来,每一个版本的修改都一定有着类库的修改,但是如果说到语言的修改,应该是仅仅当MSIL发生变动的时候,我们才可以说语言发生了修改。//仔细想了一下,这个观点有问题....但是我没找到更合适的语言来做比喻。也许应该说,只有当语法的编译规则发生改变的时候,我们才可以说语言发生了修改。

  Python也是一样,增加了方法充其量是类库的修改,而仅仅是语言的解释过程都发生了修改才可以算得上是语言层面的修改,例如从Python 2.x到Python3.x的大版本变动。

  7. 总结

  在本文中,主要是从扩展方法说起,谈到我们该怎么样更好的编写更贴近自然语言的程序。

  然后再到一些没有扩展方法语言给出的折衷实现。而对于Python,C等其他语言,我尚且没有找到合适的方法来解决问题。

  如果各位有好的办法,尤其是对于Python,毕竟这是我的工作,希望各位补充给出解决方法。

  谢谢。

时间: 2024-07-30 10:00:27

由扩展方法引“.NET研究”申出的编程思维的相关文章

一起谈.NET技术,由扩展方法引申出的编程思维

1. Helper大爆炸 .NET Framework为我们提供了丰富的类库,但是这并不是万能地,在大部分的时间,我们都需要为我们的项目特殊定制我们的通用类库. 常常,我们都可以构造一个类,类里封装一些方法.但是对于很多时候,我们并没有办法提取出这样一个类,举一个小例子,我们在很多时候,需要把url给保存到数据库里,作为一个唯一标识,但是我们知道url所占空间很大,如果用url来建立索引的话是非常耗费空间,而且影响效率的,那么我们最常用的办法就是把url做一个Hash来作为索引的替代品. 这个时

C# 3.0新特性初步研究 Part2:使用扩展方法

扩展方法(Extension Method)可以为已有的类型添加新的方法定义和实现,比如int类型目前没有一个名叫xxxyyy()的方法,那么通过使用扩展方法,我们可以为int类型添加一个xxxyyy()方法.这个有点类似于用来扩展系统功能的某些设计模式. 下面我们用代码来说话:这是我们以前的写法:    1public static class Extensions 2{ 3    public static string CamelCase(string identifier) 4{ 5  

“object”不包含“Window”的定义,并且找不到可接受类型为“object”的第一个参数的扩展方法“Window”(是否缺少 using 指令或程序集引

问题描述 mshtml.IHTMLWindow2win=this.webBrowser1.Document.Window.DomWindowasmshtml.IHTMLWindow2;为什么Window字段报错有红波浪线"object"不包含"Window"的定义,并且找不到可接受类型为"object"的第一个参数的扩展方法"Window"(是否缺少using指令或程序集引用?) 解决方案 解决方案二:估计是前面或者后面有拼写

Linq之扩展方法

目录 写在前面 系列文章 扩展方法 总结 写在前面 上篇文章介绍了隐式类型,自动属性,初始化器,匿名类的相关概念,及通过反编译的方式查看了编译器帮我们做了那些事.本篇文章将介绍扩展方法的知识点,及如何定义,如何使用的相关内容. 系列文章 Linq之Lambda表达式初步认识 Linq之Lambda进阶 Linq之隐式类型.自动属性.初始化器.匿名类 扩展方法 扩展方法使你能够向现有类型"添加"方法,而无需创建新的派生类型.重新编译或以其他方式修改原始类型. 扩展方法是一种特殊的静态方法

为什么C#不能在内部类里面声明一个扩展方法?

问题描述 C#声明一个扩展方法,必须是staticclass,static方法.我尝试把这个包含了扩展方法的类放在一个类的内部,结果编译出错了.为什么C#在语法上要有这样的限定呢? 解决方案 解决方案二:语法要求,没啥好解释的--至于为什么,这个你得问微软了,一般谁没事去研究这东西,我们又不去开发一个语言解决方案三:语法本来就这么规定的,扩展方法必须是静态方法,所在类必须是静态类.解决方案四:就是这么规定的解决方案五:扩展方法本来就是给已经有的类增加功能,如果放到内部找起来就太费事了,楼主设计的

扩展方法用法及其原理和注意事项

前言 一直以来尤其像C#一些常见的语法,本人更愿意去探讨其内部实现的原理,为什么要这么做呢?只是为了当我真正在开发中运用语法的时候不会因为犯常识性错误或者说因为一些注意事项未曾注意到而耽误一些无谓的时间,同时也能理解的更深入而不是仅仅停留在表面(或许理解也不是太透).(当然本人能力有限,太高深的东西必定是研究不明白了,也只有这能力了). 概念 扩展方法使你能够向现有类型"添加"方法,而无需创建新的派生类型.重新编译或以其他方式修改原始类型. 扩展方法是一种特殊的静态方法,但可以像扩展类

C# 3.0 新特性:扩展方法初探

C#3.0中一个激动人心的特性就是扩展方法:你可以使用实例方法的语法来调用静态方法.本文仔细阐述了这一新特性并且给出了几个相应的例子. 声明扩展方法 扩展方法的行为和静态方法是非常类似的,你只能在静态类中声明它们.为声明一个扩展方法,你需要给该方法的第一个参数指定this关键字,如下例: // Program.cspublic static class EMClass{ public static int ToInt32Ext(this string s) { return Int32.Pars

扩展方法(1) DataTable和List相互转换

最近自从公司的开发环境改了.从VS2003 改到VS2008 后 用了C# 3.0 的新东西,自己积累的方法就慢 慢的多起来了. 由于经常使用以前经常DataTable 现在都使用List<T>就此需要经常转换..用的多了,自然需要 写个简单的方法来实现互相转换 由于C#3.0出现了扩展方法 我们可以通过这样一个特性来简化我们的开发. DataTable 转换为List<T> 的我们可以通过扩展DataTable来简化 public static class DataTableEx

.NET 3.x新特性体验之扩展方法

今天我们接着看一下.NET 3.x的新特性:扩展方法.这里需要说明的是我所说的.NET 3.x的新特性,其实是C# 3.0的一些新特性,考虑到LinQ等我就把这些特性叫做.NET 3.x新特性.可能这样叫会有些歧义,但是我还是觉的这样还好,闲话不多说让我们进入正题.我觉得在C#3.0中引入自动化属性等就已经减少了我们不少负担,而扩展方法是我最喜欢的,它提供的可视化提示中显得更为巧妙.但是我也会觉得有点烦琐,当一个类的扩展方法太多时,就像有LinQ存在的情况下,会出现一大批的提示方法......