Førsterangs funksjoner i C#


onsdag 18. mai 2011 C#

I dag brukte jeg førsterangs funksjoner (first-class functions) i C# på en måte jeg følte det var verdt å skive en liten blogpost om. Jeg bruker lambda-uttrykk til mye rart hele tiden, men jeg kan faktisk ikke huske at jeg har brukt Funcs til å referere til metoder på objekter før.

Jeg skulle bruke et tredjeparts API hvor jeg skulle kalle en av to ulike metoder avhengig av en boolsk tilstandsvariabel. Metodene hadde mange parametre, men signaturen var lik på begge. Her er hvordan koden først så ut (navnene er endret for å “beskytte de skyldige”):

10 private CustomerInfo CreateOrRemove(FooWebService client,
11                                     int customerId,
12                                     string phoneNumber,
13                                     bool create)
14 {
15   if (create)
16     return client.CreateFoo("some constant",
17                             customerId,
18                             phoneNumber,
19                             string.Empty,
20                             this.someField,
21                             string.Empty,
22                             this.someOtherField);
23   else
24     return client.RemoveFoo("some constant",
25                             customerId,
26                             phoneNumber,
27                             string.Empty,
28                             this.someField,
29                             string.Empty,
30                             this.someOtherField);
31 }

Det er det at metodene tar 7 parametre som gjør koden stygg. En bedre løsning hadde selvfølgelig vært å bruke ett objekt som inneholdt de syv variablene som argument til metodene – men med tredjeparts API’er kan man ikke alltid få det som man vil.

Men jeg hater denne typen kodeduplisering. I gamledager hadde jeg latt koden forbli som dette, men etter å ha jobbet mye i funksjonelle språk i det siste er det derimot en simpel sak å fikse på problemet. Jeg var ikke helt sikker på om C# sin Func støttet det jeg ville, men det funket heldigvis helt fint (pun intended):

33 private CustomerInfo CreateOrRemove(FooWebService client,
34                                     int customerId,
35                                     string phoneNumber,
36                                     bool create)
37 {
38   Func<string, int?, string, string, string, string, string, CustomerInfo> operation;
39 
40   if (create)
41     operation = client.CreateFoo;
42   else
43     operation = client.RemoveFoo;
44 
45   return operation("some constant",
46                    customerId,
47                    phoneNumber,
48                    string.Empty,
49                    this.someField,
50                    string.Empty,
51                    this.someOtherField);
52 }

Det er ikke akkurat vakkert å spesifisere funksjonssignaturen på denne måten, men det gjør jobben – dupliseringen er borte, og koden kommuniserer faktisk litt bedre hva den gjør (etter min mening).

Trekker jeg ut valget av create/remove i en egen metode blir det enda bedre:

54 private static Func<string, int?, string, string,
55         string, string, string, CustomerInfo>
56         GetOperation(FooWebService client, bool create)
57 {
58   if (create)
59     return client.CreateFoo;
60   else
61     return client.RemoveFoo;
62 }
63 
64 private CustomerInfo CreateOrRemove(FooWebService client,
65                                     int customerId,
66                                     string phoneNumber,
67                                     bool create)
68 {
69   return GetOperation(client, create)("some constant",
70                                       customerId,
71                                       phoneNumber,
72                                       string.Empty,
73                                       this.someField,
74                                       string.Empty,
75                                       this.someOtherField);
76 }

Så det var altså dagens mest spennende kode. Har du gjort noe spenstig med Funcs i det siste? Del dine erfaringer da vel!


comments powered by Disqus