Jaunās konstrukcijas vecajā platformā
Šodien sanāca pastrādāt ar veco studiju (2005), kurā ir pieejama tikai 2.0 .Net platforma. Pēc neilgām debatēm ar projekta vadītāju, pierunāju viņus pāriet vismaz uz 2008 studiju atstājot kā pamata izpildes platformu .Net 2.0.
Tomēr ar laiku pie ērtībām pierod un šajā gadījumā ir runa par jaunajām valodas konstrukcijām C# 3.0 versijā. Jaunās iespējas ļoti saīsina kodu un atvieglo darbu. Pieradis pie C# iespējām, nolēmu nedaudz paeksperimentēt.
Šoreiz doma bija izmēģināt jaunās C# iespējas vecajā platformā. Tika apskatītas šādas konstrukcijas:
- Nenoteiktie tipi (Implicit Variables)
- Objektu un kolekciju izveides konstrukcija (Object and Collections Initializer)
- Lambda izteiksmes (Lambda Expressions)
- Daļēji definētās metodes (Partial Methods)
Nenoteiktie tipi
Zemāk ir uzrādīts koda fragments, kas izmanto nenoteiktos datu tipus:
private static void UseImplicitVariables()
{
var z = "sis ir strings";
Debug.Assert(z.Equals("sis ir strings"));
}
Kā redzams no IL, tad ģenerētais kods ir pilnībā izpildāms arī 2.0 platformā:
.method private hidebysig static void UseImplicitVariables() cil managed
{
// Code size 25 (0x19)
.maxstack 2
.locals init ([0] string z)
IL_0000: nop
IL_0001: ldstr "sis ir strings"
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldstr "sis ir strings"
IL_000d: callvirt instance bool [mscorlib]System.String::Equals(string)
IL_0012: call void [System]System.Diagnostics.Debug::Assert(bool)
IL_0017: nop
IL_0018: ret
} // end of method Program::UseImplicitVariables
Objektu un kolekciju izveides konstrukcija un Lambda izteiksmes
Zemāk ir uzrādīts koda fragments, kas izmanto šo konstrukciju:
private static void UseObjectInitializer()
{
List<Customer> list = new List<Customer>
{
new Customer { Id = 1, Name = "vards 1" },
new Customer { Id = 2, Name = "vards 2" }
};
Debug.Assert(list.Count == 2);
Customer found = list.Find(c => c.Id == 1);
Debug.Assert(found.Id == 1);
Customer cc = new Customer { Id = 1, Name = "vards" };
Debug.Assert(cc.Id == 1);
}
Kā redzams no IL, tad ģenerētais kods ir pilnībā izpildāms arī 2.0 platformā:
.method private hidebysig static void UseObjectInitializer() cil managed
{
// Code size 201 (0xc9)
.maxstack 4
.locals init ([0] class [mscorlib]System.Collections.Generic.List`1<class CSharp20Test.Customer> list,
[1] class CSharp20Test.Customer found,
[2] class CSharp20Test.Customer cc,
[3] class [mscorlib]System.Collections.Generic.List`1<class CSharp20Test.Customer> '<>g__initLocal1',
[4] class CSharp20Test.Customer '<>g__initLocal2',
[5] class CSharp20Test.Customer '<>g__initLocal3',
[6] class CSharp20Test.Customer '<>g__initLocal4')
IL_0000: nop
IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<class CSharp20Test.Customer>::.ctor()
IL_0006: stloc.3
IL_0007: ldloc.3
IL_0008: newobj instance void CSharp20Test.Customer::.ctor()
IL_000d: stloc.s '<>g__initLocal2'
IL_000f: ldloc.s '<>g__initLocal2'
IL_0011: ldc.i4.1
IL_0012: callvirt instance void CSharp20Test.Customer::set_Id(int32)
IL_0017: nop
IL_0018: ldloc.s '<>g__initLocal2'
IL_001a: ldstr "vards 1"
IL_001f: callvirt instance void CSharp20Test.Customer::set_Name(string)
IL_0024: nop
IL_0025: ldloc.s '<>g__initLocal2'
IL_0027: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<class CSharp20Test.Customer>::Add(!0)
IL_002c: nop
...
Kas attiecas uz lambda izteiksmēm, tad tās tiek konvertētas uz predikātu ar delegātu, kas smuki darbojas arī 2.0 platformā:
IL_0065: ldsfld class [mscorlib]System.Predicate`1<class CSharp20Test.Customer> CSharp20Test.Program::'CS$<>9__CachedAnonymousMethodDelegate6'
IL_006a: brtrue.s IL_007f
IL_006c: ldnull
IL_006d: ldftn bool CSharp20Test.Program::'<UseObjectInitializer>b__5'(class CSharp20Test.Customer)
IL_0073: newobj instance void class [mscorlib]System.Predicate`1<class CSharp20Test.Customer>::.ctor(object,
native int)
IL_0078: stsfld class [mscorlib]System.Predicate`1<class CSharp20Test.Customer> CSharp20Test.Program::'CS$<>9__CachedAnonymousMethodDelegate6'
IL_007d: br.s IL_007f
IL_007f: ldsfld class [mscorlib]System.Predicate`1<class CSharp20Test.Customer> CSharp20Test.Program::'CS$<>9__CachedAnonymousMethodDelegate6'
IL_0084: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<class CSharp20Test.Customer>::Find(class [mscorlib]System.Predicate`1<!0>)
IL_0089: stloc.1
Daļēji definētās metodes
Zemāk ir attēlots koda fragments, kas izmanto dalītās metodes konstrukciju:
public partial class Customer
{
public int Id { get; set; }
public string Name { get; set; }
partial void Check();
public void Save()
{
Check();
}
}
public partial class Customer
{
partial void Check()
{
// do some checking
Console.Write(" .... Checking client: " + this.Id);
}
}
IL kods arī saprotams, ka izpildīsies arī uz vecākām versijām:
.method public hidebysig instance void Save() cil managed
{
// Code size 9 (0x9)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call instance void CSharp20Test.Customer::Check()
IL_0007: nop
IL_0008: ret
} // end of method Customer::Save
Ja turpretim, metodes ķermenis nebūtu definēts, tad IL kods izradītos pavisam vienkāršs:
.method public hidebysig instance void Save() cil managed
{
// Code size 2 (0x2)
.maxstack 8
IL_0000: nop
IL_0001: ret
} // end of method Customer::Save
Kāpēc visas šīs konstrukcijas ir pieejamas arī vecajās platformas versijās? Tāpēc, ka neatkarīgi no mērķa .Net platformas, ko var izvēlēties pie projekta īpašībām, kods tiek kompilēts ar 3.5 platformas C# kompilatoru.
Vienīgā konstrukcija, kas neizpildijās ne 3.0, ne arī 2.0 platformas versijās bija paplašinājuma metodes (Extension Methods), jo programma nemaz nekompilējās, jo nepieciešamās klases atrodas System.Core asemblijā, kas ir pieejams tikai 3.5 platformā.
Secinājums: izmantojiet jaunās C# valodas iespējas jaunā studijā un izpildiet kodu bez bežām uz serveriem ar 2.0 platformu, jo jaunās konstrukcijas ir tikai kompilācijas laika transformācija.
Cerams, ka noderēs!