设计情势六大标准(2):里氏替换原则

四、Extension Method的本质

设计形式六大规格(2):里氏替换原则 

通过下面一节的牵线,我们领略了在C#中哪些去定义一个Extension
Method:它是概念在一个Static
class
中的、第一个Parameter标记为this重点字的Static
Method
。在这一节中,我们来更为认识Extension Method。

 

和C# 3.0的其余新特色相似,Extension Method仅仅是C#那种.NET Programming
Language的新特点而已。大家掌握,C#是一种典型的编写翻译型的言语,我们编辑的Source
Code必须先通过和C# Compiler编写翻译成Assembly,才能被CLR加载,被JIT
编写翻译成Machine Instruction并最后被实施。C#
3.0的那么些新的特色大都影响Source被C#
Compiler编译成Assembly这一个阶段,换句话说,这几个新特仅仅是Compiler的新特征而已。通过对Compiler实行校对,促使他将C#
3.0引入的新的语法编写翻译成相对应的IL Code,从本质上看,这几个IL Code
和原先的IL并没有实质的界别。全部当被编写翻译生成成Assembly被CLHighlander加载、执行的时候,CL智跑是发现不到这个新的天性的。

一定有为数不少人跟本人刚看到那项条件的时候同样,对那个规格的名字充满狐疑。其实原因便是那项条件最早是在一九八六年,由北达科他香槟分校学院的1人姓里的女生(BarbaraLiskov)提出来的。

从Extension Method的概念我们可观察,Extension Method本质上是2个Static
Method。不过我们反复以Instance Method的法子进行调用。C#
Compiler的功力很引人注目:把八个以Instance Method格局调用的Source
Code编写翻译成的于对应于守旧的Static Method调用的IL Code

概念1:如若对每三个档次为 T1的靶子 o1,都有档次为 T2 的对象o2,使得以
T1概念的有着程序 P 在装有的目的 o1 都代换到 o2 时,程序 P
的一言一动并未发生变化,那么类型 T2 是类别 T1 的子类型。

固然Extension Method本质上单独是一个Static Class的Static
Method成员,不过终究和价值观的Static
Method有所分化:在第三个Parameter前加了3个this根本字。大家后天来看望她们之间的微薄的差距。大家先定义3个貌似的Static
Method:

概念2:全数引用基类的地点必须能透明地利用其子类的对象。

public static Vector Adds(Vector v, Vector v1)
{
  return new Vector { X = v.X + v1.X, Y = v.Y + v1.Y };
}

标题由来:有一职能P1,由类A完成。现要求将效率P1实行扩张,扩张后的效果为P,个中P由原有职能P1与新作用P2组成。新功用P由类A的子类B来形成,则子类B在成就新功用P2的还要,有或然会导致原本成效P1发生故障。

注:Vector的概念参见《深入掌握C# 3.0的新特色(2):Extension Method –
Part
I
》。

解决方案:当使用持续时,服从里氏替换原则。类B继承类A时,除添加新的艺术成功新增功效P2外,尽量不要重写父类A的格局,也硬着头皮不要重载父类A的方法。

大家来看望通过Compiler进行编写翻译生成的IL:

继承蕴涵这样一层含义:父类中凡是已经完毕好的不二法门(相对于肤浅方法而言),实际上是在设定一文山会海的正规和契约,固然它不强制须要拥有的子类必须听从这一个契约,可是若是子类对这一个非抽象方法任意修改,就会对一切继承连串造成破坏。而里氏替换原则正是发挥了这一层意思。

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       50 (0x32)
  .maxstack  2
  .locals init ([0] class Artech.ExtensionMethod.Vector v,
           [1] class Artech.ExtensionMethod.Vector ‘<>g__initLocal0’)
  IL_0000:  nop
  IL_0001:  newobj     instance void Artech.ExtensionMethod.Vector::.ctor()
  IL_0006:  stloc.1
  IL_0007:  ldloc.1
  IL_0008:  ldc.r8     1.
  IL_0011:  callvirt   instance void Artech.ExtensionMethod.Vector::set_X(float64)
  IL_0016:  nop
  IL_0017:  ldloc.1
  IL_0018:  ldc.r8     2.
  IL_0021:  callvirt   instance void Artech.ExtensionMethod.Vector::set_Y(float64)
  IL_0026:  nop
  IL_0027:  ldloc.1
  IL_0028:  stloc.0
  IL_0029:  ldloc.0
  IL_002a:  ldloc.0
  IL_002b:  call       class Artech.ExtensionMethod.Vector Artech.ExtensionMethod.Extension::Adds(class Artech.ExtensionMethod.Vector,
class Artech.ExtensionMethod.Vector)
  IL_0030:  stloc.0
  IL_0031:  ret
} // end of method Program::Main

三番五次作为面向对象三大特色之一,在给程序设计带来巨大便利的还要,也推动了弊端。比如利用持续会给程序带来侵入性,程序的可移植性下落,增添了指标间的耦合性,假设二个类被别的的类所继承,则当那些类供给修改时,必须考虑到全数的子类,并且父类修改后,全部涉嫌到子类的效劳都有大概会生出故障。

对掌握IL的人来说,对地方的IL code应该很容易精通。

举例表达继承的高危害,大家须要做到1个两数相减的效用,由类A来负担。

我们再来看看对于通过上面包车型地铁不二法门定义的Extension Method:

class A{
    public int func1(int a, int b){
        return a-b;
    }
}

public class Client{
    public static void main(String[] args){
        A a = new A();
        System.out.println("100-50="+a.func1(100, 50));
        System.out.println("100-80="+a.func1(100, 80));
    }
} 

起名 1public static class Extension
起名 2起名 3    起名 4{
起名 5         public static Vector Adds(this Vector v, Vector v1)
起名 6起名 7        起名 8{
起名 9起名 10            return new Vector 起名 11{ X = v.X + v1.X, Y = v.Y + v1.Y };
起名 12        }
起名 13}
起名 14

运作结果:

对于得IL如下:

100-50=50

起名 15.method public hidebysig static class Artech.ExtensionMethod.Vector 
起名 16Adds(class Artech.ExtensionMethod.Vector v,
起名 17class Artech.ExtensionMethod.Vector v1) cil managed
起名 18起名 19起名 20{
起名 21  .custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
起名 22  // Code size       53 (0x35)
起名 23  .maxstack  3
起名 24  .locals init ([0] class Artech.ExtensionMethod.Vector ‘<>g__initLocal0’,
起名 25           [1] class Artech.ExtensionMethod.Vector CS$1$0000)
起名 26  IL_0000:  nop
起名 27  IL_0001:  newobj     instance void Artech.ExtensionMethod.Vector::.ctor()
起名 28  IL_0006:  stloc.0
起名 29  IL_0007:  ldloc.0
起名 30  IL_0008:  ldarg.0
起名 31  IL_0009:  callvirt   instance float64 Artech.ExtensionMethod.Vector::get_X()
起名 32  IL_000e:  ldarg.1
起名 33  IL_000f:  callvirt   instance float64 Artech.ExtensionMethod.Vector::get_X()
起名 34  IL_0014:  add
起名 35  IL_0015:  callvirt   instance void Artech.ExtensionMethod.Vector::set_X(float64)
起名 36  IL_001a:  nop
起名 37  IL_001b:  ldloc.0
起名 38  IL_001c:  ldarg.0
起名 39  IL_001d:  callvirt   instance float64 Artech.ExtensionMethod.Vector::get_Y()
起名 40  IL_0022:  ldarg.1
起名 41  IL_0023:  callvirt   instance float64 Artech.ExtensionMethod.Vector::get_Y()
起名 42  IL_0028:  add
起名 43  IL_0029:  callvirt   instance void Artech.ExtensionMethod.Vector::set_Y(float64)
起名 44  IL_002e:  nop
起名 45  IL_002f:  ldloc.0
起名 46  IL_0030:  stloc.1
起名 47  IL_0031:  br.s       IL_0033
起名 48  IL_0033:  ldloc.1
起名 49  IL_0034:  ret
起名 50} // end of method Extension::Adds
起名 51

100-80=20

透过比较,大家发现和方面定义的一般的Static
Method生成的IL唯一的界别正是:在Adds方法定义最开始添加了上边一段代码:

后来,我们须求扩张八个新的功效:完毕两数相加,然后再与100求和,由类B来顶住。即类B须要做到三个效益:

起名 52.custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 

  • 两数相减。
  • 两数相加,然后再加100。

这段添加的IL代码很肯定,就是在Adds方法上添加3个Customer
Attribute:System.Runtime.CompilerServices.ExtensionAttribute。ExtensionAttribute具有如下的概念:

由于类A已经达成了第②个成效,所以类B继承类A后,只需求再形成第3个职能就能够了,代码如下:

起名 53[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)]
起名 54public sealed class ExtensionAttribute : Attribute
起名 55起名 56起名 57{
起名 58}
起名 59

class B extends A{
    public int func1(int a, int b){
        return a+b;
    }

    public int func2(int a, int b){
        return func1(a,b)+100;
    }
}

public class Client{
    public static void main(String[] args){
        B b = new B();
        System.out.println("100-50="+b.func1(100, 50));
        System.out.println("100-80="+b.func1(100, 80));
        System.out.println("100+20+100="+b.func2(100, 20));
    }
} 

于是上边Extension Method的定义

类B实现后,运转结果:

起名 60public static Vector Adds(this Vector v, Vector v1)
起名 61起名 62起名 63{
起名 64起名 65            return new Vector 起名 66{ X = v.X + v1.X, Y = v.Y + v1.Y };
起名 67}
起名 68

100-50=150

和下部的定义是千篇一律的

100-80=180

起名 69[ExtensionAttribute]
起名 70public static Vector Adds(Vector v, Vector v1) 
起名 71起名 72起名 73{
起名 74起名 75            return new Vector 起名 76{ X = v.X + v1.X, Y = v.Y + v1.Y };
起名 77}
起名 78

100+20+100=220

唯独,System.Runtime.CompilerServices.ExtensionAttribute和此外Custom
Attribute不等同,因为它是为了Extension
Method的而定义的,大家只好通过抬高this Key word的语法来定义Extension
Method。所以当大家将System.Runtime.Compiler瑟维斯s.ExtensionAttribute直接动用到Adds方法会产出下面包车型大巴Compile
Error:

咱俩发现原来运维寻常的相减功用产生了错误。原因便是类B在给艺术起名时无意中重写了父类的格局,造成全部运营相减成效的代码全体调用了类B重写后的法子,造成原本运维如常的效能现身了错误。在本例中,引用基类A完成的效率,换到子类B之后,发生了特别。在骨子里编制程序中,我们常常会由此重写父类的法门来形成新的功效,那样写起来即使简单,然而凡事继承连串的可复用性会比较差,尤其是运用多态相比较频仍时,程序运转出错的可能率很大。假如非要重写父类的不二法门,比较通用的做法是:原来的父类和子类都卫冕贰个更易懂的基类,原有的持续关系去掉,采取注重、聚合,组合等涉及代替。

起名 79Do not use ‘System.Runtime.CompilerServices.ExtensionAttribute’. Use the ‘this’ keyword instead.

里氏替换原则通俗的来讲就是:子类能够扩充父类的效率,但不能够改变父类原有的效率。它富含以下4层含义:

地点大家相比了Extension Method自个儿IL和一般Static Method
IL,将来大家看看当大家以Instance Method格局调用Extension
Method的IL。要是大家经过上面包车型大巴章程调用Adds。 

  • 子类能够兑现父类的空洞方法,但不能够覆盖父类的非抽象方法。
  • 子类中得以追加本身特有的办法。
  • 当子类的法门重载父类的章程时,方法的放置条件(即方法的形参)要比父类方法的输入参数更宽大。
  • 当子类的方式达成父类的抽象方法时,方法的前置条件(即方法的再次来到值)要比父类更严格。

起名 80class Program
起名 81起名 82    起名 83{
起名 84        static void Main(string[] args)
起名 85起名 86        起名 87{
起名 88起名 89 var v = new Vector 起名 90{ X = 1, Y = 2 };
起名 91           v = v.Adds(v);
起名 92        }
起名 93}
起名 94

看上去很不可名状,因为我们会意识在融洽编程中经常会背离里氏替换原则,程序依然跑的可观的。所以我们都会时有发生这么的疑云,假若笔者非要不服从里氏替换原则会有怎样结果?

下面是Main Method的IL:

下文就是:你写的代码出难题的概率将会大大增添。

起名 95.method private hidebysig static void  Main(string[] args) cil managed
起名 96起名 97起名 98{
起名 99  .entrypoint
起名 100  // Code size       50 (0x32)
起名 101  .maxstack  2
起名 102  .locals init ([0] class Artech.ExtensionMethod.Vector v,
起名 103           [1] class Artech.ExtensionMethod.Vector ‘<>g__initLocal0’)
起名 104  IL_0000:  nop
起名 105  IL_0001:  newobj     instance void Artech.ExtensionMethod.Vector::.ctor()
起名 106  IL_0006:  stloc.1
起名 107  IL_0007:  ldloc.1
起名 108  IL_0008:  ldc.r8     1.
起名 109  IL_0011:  callvirt   instance void Artech.ExtensionMethod.Vector::set_X(float64)
起名 110  IL_0016:  nop
起名 111  IL_0017:  ldloc.1
起名 112  IL_0018:  ldc.r8     2.
起名 113  IL_0021:  callvirt   instance void Artech.ExtensionMethod.Vector::set_Y(float64)
起名 114  IL_0026:  nop
起名 115  IL_0027:  ldloc.1
起名 116  IL_0028:  stloc.0
起名 117  IL_0029:  ldloc.0
起名 118  IL_002a:  ldloc.0
起名 119  IL_002b:  call       class Artech.ExtensionMethod.Vector Artech.ExtensionMethod.Extension::Adds(class Artech.ExtensionMethod.Vector,
起名 120class Artech.ExtensionMethod.Vector)
起名 121  IL_0030:  stloc.0
起名 122  IL_0031:  ret
起名 123} // end of method Program::Main
起名 124

 

透过地方的IL,大家来看调用的是Artech.ExtensionMethod.Extension的Adds方法。

摘自 http://www.uml.org.cn/sjms/201211023.asp\#2

起名 125IL_002b:  call class Artech.ExtensionMethod.Vector Artech.ExtensionMethod.Extension::Adds(class Artech.ExtensionMethod.Vector,
起名 126class Artech.ExtensionMethod.Vector)
起名 127

由此对IL的解析,大家大多看到了Extension
Method的本来面目。大家再来不难描述一下对Compiler的编写翻译进程:当Compiler对Adds方法的调用进行编写翻译的进程的时候,它必须认清这几个Adds情势是Vector
Type的成员
还是以Extension Method的章程定义。Extension
Method的先期级是低于的,只有分明Vector中并未定义相应的Adds方法的时候,Compiler才会在引用的Namespace中查看那么些Namespace中是不是定义有照应的Adds
Extension Method的Static
Class。找到后作举办相应的编写翻译,不然出现编写翻译错误。

⑤ 、三个整机的Extension Method的山姆ple

在介绍了Extension
Method的昆仑山真面目之后,大家透过2个对峙完整的Sample进一步通晓Extension
Method的采用,通过这一个Sample,我们还足以省略领会LINQ的规律。

C# 3.0为LINQ定义了一多级的Operator:select, from,where,orderby…,
促使我们根据OO的措施来处理盘根错节的数目,比如XML,Relational DB
Data,C#中IEnumeratable<T> Object。比如:

起名 128起名 129var names = new List<string> 起名 130{ “Tom Cruise”, “Tom Hanks”, “Al Pacino”, “Harrison Ford” };
起名 131var result = names.Where(name => name.StartsWith(“Tom”));
起名 132foreach(var name in result)
起名 133起名 134起名 135{
起名 136      Console.WriteLine(name);
起名 137}
起名 138

我们透过上边的Code,从一类别的人名列表中(”汤姆 Cruise”, “汤姆 Hanks”, “Al
Pacino”, “Harrison Ford”)筛选名字(First Name)为汤姆的全名。通过Where
Operator,传入三个以Lambda Expression代表的筛选标准(name =>
name.StartsWith(“Tom”))。
Where Operator正是通过Extension
Method的不二法门定义的。

在此间提供的Sample正是概念八个形成Where
Operator相同效能的Operator,我们把那个Operator起名为When

起名 139using System;
起名 140using System.Collections.Generic;
起名 141using System.Linq;
起名 142using System.Text;
起名 143using System.Collections;
起名 144
起名 145namespace Artech.ExtensionMethod
起名 146起名 147起名 148{
起名 149    public delegate TResult Function<Tparam, TResult>(Tparam param);
起名 150
起名 151    public static class Extension
起名 152起名 153    起名 154{
起名 155        public static IEnumerable<TSource> When<TSource>(this IEnumerable<TSource> source, Function<TSource, bool> predicate)
起名 156起名 157        起名 158{
起名 159            return new WhenEnumerator<TSource>(source, predicate);
起名 160        }  
起名 161
起名 162    }
起名 163
起名 164    public class WhenEnumerator<TSource> : IEnumerable<TSource>, IEnumerator<TSource>
起名 165起名 166    起名 167{
起名 168        private IEnumerable<TSource> _source;
起名 169        private Function<TSource, bool> _predicate;
起名 170        private IEnumerator<TSource> _sourceEnumerator;
起名 171
起名 172        public WhenEnumerator(IEnumerable<TSource> source, Function<TSource, bool> predicate)
起名 173起名 174        起名 175{
起名 176            this._source = source;
起名 177            this._predicate = predicate;
起名 178            this._sourceEnumerator = this._source.GetEnumerator();
起名 179        }
起名 180
起名 181起名 182        IEnumerable Members#region IEnumerable<TSource> Members
起名 183
起名 184        public IEnumerator<TSource> GetEnumerator()
起名 185起名 186        起名 187{
起名 188            return new WhenEnumerator<TSource>(this._source, this._predicate);
起名 189        }
起名 190
起名 191        #endregion
起名 192
起名 193起名 194        IEnumerable Members#region IEnumerable Members
起名 195
起名 196        IEnumerator IEnumerable.GetEnumerator()
起名 197起名 198        起名 199{
起名 200            throw new Exception(“The method or operation is not implemented.”);
起名 201        }
起名 202
起名 203        #endregion
起名 204
起名 205起名 206        IEnumerator Members#region IEnumerator<TSource> Members
起名 207
起名 208        public TSource Current
起名 209起名 210        起名 211{
起名 212起名 213            get 起名 214{ return this._sourceEnumerator.Current; }
起名 215        }
起名 216
起名 217        #endregion
起名 218
起名 219起名 220        IDisposable Members#region IDisposable Members
起名 221
起名 222        public void Dispose()
起名 223起名 224        起名 225{
起名 226            //throw new Exception(“The method or operation is not implemented.”);
起名 227        }
起名 228
起名 229        #endregion
起名 230
起名 231起名 232        IEnumerator Members#region IEnumerator Members
起名 233
起名 234        object IEnumerator.Current
起名 235起名 236        起名 237{
起名 238            get
起名 239起名 240            起名 241{
起名 242                return this._sourceEnumerator.Current;
起名 243            }
起名 244        }
起名 245
起名 246        public bool MoveNext()
起名 247起名 248        起名 249{
起名 250            if (!this._sourceEnumerator.MoveNext())
起名 251起名 252            起名 253{
起名 254                return false;
起名 255            }
起名 256
起名 257            while (!this._predicate(this._sourceEnumerator.Current))
起名 258起名 259            起名 260{
起名 261                if (!this._sourceEnumerator.MoveNext())
起名 262起名 263                起名 264{
起名 265                    return false;
起名 266                }
起名 267            }
起名 268
起名 269            return true;
起名 270        }
起名 271
起名 272        public void Reset()
起名 273起名 274        起名 275{
起名 276            this._sourceEnumerator.Reset();
起名 277        }
起名 278
起名 279        #endregion
起名 280    }
起名 281}
起名 282

作者们来探视大家新的LINQ Operator:When的概念。笔者第③定义了七个Generic
Delegate:Function。实际上她定义了一个一元函数y = f(x),TParam和TResult为参数和重回值得类型。 

起名 283public delegate TResult Function<Tparam, TResult>(Tparam param);

继之在Static Class Extesnion中定义了Extension
Method:When。该措施包含七个参数,在那之中1个是实践筛选的数据源,另二个是用来判断数据源每种对象是否满意你所定义的筛选标准的断言。再次回到一个大家自定义的、完成了IEnumerable的WhenEnumerator对象。

起名 284public static class Extension
起名 285起名 286    起名 287{
起名 288        public static IEnumerable<TSource> When<TSource>(this IEnumerable<TSource> source, Function<TSource, bool> predicate)
起名 289起名 290        起名 291{
起名 292            return new WhenEnumerator<TSource>(source, predicate);
起名 293        } 
起名 294    }
起名 295

WhenEnumerator的定义是兑现When Extension
Method的严重性,大家前日任重(Ren Zhong)而道远来介绍它的具体贯彻。WhenEnumerator达成了Interface
Enumerable<T>,
为了简单,咱们也它对应的Enumerator的贯彻也定义在同二个Class中,所以WhenEnumerator达成了五个Interface:IEnumerable<TSource>,
IEnumerator<TSource>。
 

以下2个成员分头表示:用于实施筛选的数据源、用于判断是不是知足筛选标准的断言以及数据源的Enumerator对象。

起名 296private IEnumerable<TSource> _source;
起名 297private Function<TSource, bool> _predicate;
起名 298private IEnumerator<TSource> _sourceEnumerator;
起名 299

因此重临2个WhenEnumerator对象,实现了IEnumerable<TSource>的GetEnumerator()方法。 

起名 300        public IEnumerator<TSource> GetEnumerator()
起名 301起名 302        起名 303{
起名 304            return new WhenEnumerator<TSource>(this._source, this._predicate);
起名 305        }
起名 306

对于另1个Interface
IEnumerator<TSource>,直接调用数据源的Enumerator的同名方法达成了Current,和Reset()。对于MoveNext()则通过如下的办法落成:把当下的职位设置在下多少个满意筛选标准的Element上

起名 307public bool MoveNext()
起名 308起名 309        起名 310{
起名 311            if (!this._sourceEnumerator.MoveNext())
起名 312起名 313            起名 314{
起名 315                return false;
起名 316            }
起名 317
起名 318            while (!this._predicate(this._sourceEnumerator.Current))
起名 319起名 320            起名 321{
起名 322                if (!this._sourceEnumerator.MoveNext())
起名 323起名 324                起名 325{
起名 326                    return false;
起名 327                }
起名 328            }
起名 329
起名 330            return true;
起名 331        }
起名 332

到现行反革命告竣,那么些新的LINQ Operator被创设,未来我们得以依据使用Where
operator的情势来调用When。

大家能够通过Delegate的办法来行使When Operator:

起名 333class Program
起名 334起名 335    起名 336{
起名 337        static void Main()
起名 338起名 339        起名 340{
起名 341起名 342            var names = new List<string> 起名 343{ “Tom Cruise”, “Tom Hanks”, “Al Pacino”, “Harrison Ford” };
起名 344起名 345            var result = names.When(delegate(string name) 起名 346{ return name.StartsWith(“Tom”); });
起名 347            foreach (var name in result)
起名 348起名 349            起名 350{
起名 351                Console.WriteLine(name);
起名 352            }
起名 353        }
起名 354}
起名 355

出口结果:

起名 356Tom Cruise
起名 357Tom Hanks
起名 358

大家也可以经过Lambda Expression的艺术来选择When Operator:

起名 359static void Main()
起名 360起名 361        起名 362{
起名 363起名 364            var names = new List<string> 起名 365{ “Tom Cruise”, “Tom Hanks”, “Al Pacino”, “Harrison Ford” };
起名 366            var result = names.When(name=>name.StartsWith(“Tom”));
起名 367            foreach (var name in result)
起名 368起名 369            起名 370{
起名 371                Console.WriteLine(name);
起名 372            }
起名 373        }
起名 374

肯定那种措施更简明。 

Deferred Evaluation

对此LINQ,有二个老大主要的性状:Deferred
伊娃luation。在询问那特性格以前,大家来看二个事例:

起名 375static void Main()
起名 376起名 377起名 378{
起名 379起名 380            var names = new List<string> 起名 381{ “Tom Cruise”, “Tom Hanks”, “Al Pacino”, “Harrison Ford” };
起名 382            var result1 = names.When(name=>name.StartsWith(“Tom”));
起名 383            names[0] = “Stephen Chou”;
起名 384            var result2 = names.When(name => name.StartsWith(“Tom”));
起名 385
起名 386
起名 387            foreach (var name in result1)
起名 388起名 389            起名 390{
起名 391                Console.WriteLine(name);
起名 392            }
起名 393
起名 394            foreach (var name in result2)
起名 395起名 396            起名 397{
起名 398                Console.WriteLine(name);
起名 399            }
起名 400}
起名 401

运维程序,你会发现三个foreach loop展现的结果都以同一的:Tom汉克s。为何result1实在第一个Element被改动以前重临的,但大家最终输出的结果却反映的是改变之后的数据源。通过大家地点的定义,你很不难获取答案。在那里笔者要说的是LINQ的叁个关键的特点Deferred
伊娃luation:在调用Operator的时候并不会有别的的别样数据得到的经过,这几个等级的天职是创办八个同于获取数据的表明式。只要您真的所用到这些数量的时候,选拔重数据源中通过你营造的表达式通过询问获取数据。

C# 3.x相关内容:
[原创]深深明白C#
3.x的新特点(1):Anonymous
Type

[原创]深深掌握C#
3.x的新特点(2):Extension Method – Part
I

[原创]深远通晓C#
3.x的新特征(2):Extension Method – Part
II

[原创]深刻通晓C#起名, 3.x的新特征(3):从Delegate、Anonymous
Method到拉姆da
Expression

[原创]深刻精晓C# 3.x的新特征(4):Automatically Implemented
Property

[原创]深远通晓C# 3.x的新特征(5):Object Initializer 和 Collection
Initializer

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图