65.9K
CodeProject 正在变化。 阅读更多。
Home

10 分钟揭秘 LINQ

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.62/5 (32投票s)

2008年6月26日

CPOL

4分钟阅读

viewsIcon

100307

downloadIcon

596

在10/15分钟内了解C#到LINQ的演变过程

引言

许多优秀的文章都出色地解释了LINQ。从语法概念再到项目,LINQ的内容已经非常丰富。本文的目的不是重复/重组这些材料。有关详细信息,请参阅好书列表。我个人最喜欢的是这本小小的口袋参考书

本文的概念基于这个短视频。本文将以图解的形式展示我们如何发展到LINQ,以及C#从1.0到3.5的演变过程。LINQ是新事物,还是仅仅是对旧的丑陋代码的优雅语法?在本文中,我们将用不到10/15分钟的时间,回顾C#到LINQ的演变历程。让我们开始吧,揭开LINQ的神秘面纱。

我们将需要一些基础代码。我们将使用如下的City

class City
    {
        private string _name;
        private string _state;

        public City(string name, string state)
        {
            this._name = name;
            this._state = state;
        }

        public string Name
        {
            get
            {
                return _name;
            }
        }

        public string State
        {
            get
            {
                return _state;
            }
        }
    }

让我们创建一个城市集合,然后使用foreach遍历这个集合,如下所示

List<City> cities = new List<City>();
City c = new City("Santa Ana", "CA");
City c1 = new City("Irvine", "CA");
City c2 = new City("Bloomington", "IN");

cities.Add(c);
cities.Add(c1);
cities.Add(c2);

foreach (City tempCity in cities)
{
    Console.WriteLine("City Name : {0} and State : {1}",tempCity.Name, tempCity.State);
}

让我们根据某些条件过滤这个集合。比如,我们只想显示加州的城市。一个非常简单的解决方案是在foreach循环中添加一个if条件,如下所示

 if (tempCity.State == "CA")
   Console.WriteLine("City Name : {0} and State : {1}", tempCity.Name, tempCity.State);

这是一个不错的解决方案。但是这段代码的问题在于“紧耦合”。C# 1.0为这个问题提供了一种基于委托的可组合解决方案。以下是重构后的代码版本

步骤 1

delegate bool IsParticularState(City c);

第二步

static bool IsCalifornia(City c)
     {
         return c.State == "CA" ? true : false;
     }

步骤 3

PrintCityInfo(cities, new IsParticularState(IsCalifornia));

步骤 4

static void PrintCityInfo(List _cities, IsParticularState filter)
        {
            foreach (City localCity in _cities)
            {
                if (filter(localCity))
                {
                    Console.WriteLine("City Name : {0} and State : {1}", 
                                      localCity.Name, localCity.State);
                }
            }
        }

如步骤1所示,我们添加了一个delegate IsParticularState。步骤2是这个delegate的目标方法。步骤3是重构后的foreach循环调用,它接收一个delegatecities集合作为输入参数。步骤4将输出city namestate

注意这里的filter代替了简单的if条件。filter(localCity)是一个delegate调用。通过这样做,我们将过滤逻辑解耦到了一个独立的方法中

static bool IsCalifornia(City c)

对于实现简单的过滤逻辑来说,这仍然是太多的代码。如果我们不必添加额外的过滤方法,那岂不是更好?是的,这正是C# 2.0匿名方法的作用所在。因此,上面的代码可以重构为如下形式

PrintCityInfo(cities,delegate(City ctemp){return (ctemp.State == "CA"?true:false);});

注意内联的delegate和匿名方法。这是C# 2.0的一个特性——匿名,因为这个方法没有名称。

这是一个不错的改进,但仍然需要delegate代码。如果我们想使用C# 3.0来改进这段代码呢?我们可以使用lambda表达式代替匿名方法,如下所示

PrintCityInfo(cities,ctemp=>ctemp.State=="CA");

这段代码难道不优雅吗?从C# 1.0中的7+行代码、delegate和目标方法调用,到C# 3.0中不到半行的代码。这就是lambda表达式的强大之处。底层编译器为我们处理了所有的繁重工作。Lambda表达式是隐式变量和匿名方法的结合。在我们的例子中,ctemp是一个隐式变量,而ctemp.State=="CA"是一个匿名方法。

现在,LINQ登场了。如果我们想对最终结果应用多个过滤条件,或者想根据预定义的顺序(如city name)对输出进行排序呢?在没有LINQ的情况下,我们也可以做到。但LINQ提供了一个优雅的解决方案,如下所示

PrintCityInfousingLINQ(from ctemp in cities 
                       where ctemp.State=="CA"
                       select ctemp);

static void PrintCityInfousingLINQ(IEnumerable _cities)
        {
            foreach (City localCity in _cities)
            {
                
               Console.WriteLine("City Name : {0} and State : {1}", 
                    localCity.Name, localCity.State);
               
            }
        }

在这里,我们定义了一个新方法PrintCityInfousingLINQ。它与PrintCityInfo方法相同。PrintCityInfousingLINQ接收一个enumerator作为输入参数,而不是接收一个集合和delegate。另一个重要因素是泛型。泛型增加了类型安全性,并提供了所有其他有据可查的好处

让我们按city name对最终结果进行排序。这在TSQL中是一个典型的order by子句。使用LINQ,可以这样写

PrintCityInfousingLINQ(from ctemp in cities 
                       where ctemp.State=="CA"
                       orderby ctemp.Name 
                       select ctemp);

没有orderby子句,圣安娜将是第一个结果。有了order by子句,尔湾将是第一个记录。

结论

从C# 1.0中简单的if条件到C# 3.0中的LINQ,存在着逻辑和语法的连续演进。想象一下用……

from ctemp in cities 
where ctemp.State=="CA"
orderby ctemp.Name 
select ctemp;

……if条件或delegate来编写。LINQ在语法方面带来了急需的改进,并且支持SQL、XML和对象。有了LINQ,意图与语言语法更紧密地匹配。

在一个研究生院空旷的停车场午夜辩论中,有人说了这样一句话——“任何编程语言的力量都在于它能够非常轻松地说出非常复杂的事情。就像“树林可爱,幽暗又深邃,但我有诺言要遵守……”

LINQ是朝着这个方向迈出的又一步。你觉得呢?

更新:在向来自传统过程式编程背景的人解释LINQ时,我使用了这个例子来解释Lambda基础。希望这对其他人也有用——请告诉我——LINQPad用于测试此代码

delegate bool diseven(int i);

void Main(){
    int[] mi = new int[]{2,3,4,5,1};
    "Count Even Numbers".Dump();
    diseven di =isEven;
    int counter=0;
    foreach(int i in mi)
     if(di(i))
       counter++;
    counter.Dump("using Traditional Loop");
    mi.Count(delegate(int i){
    return i%2==0;
    }).Dump("Using delegate");
    Func<int,bool> fm = i =>i%2==0;
    mi.Count(i=>fm(i)).Dump("using built in delegate Func");
    mi.Count(i=>i%2==0).Dump("using Lambda");
}

bool isEven(int i)
{
return i%2==0;
}

历史

  • 2008年6月26日:初始帖子
  • 2014年6月22日:包含Lambda示例
© . All rights reserved.