C# versio 7 – milloin hyvä ohjelmointikieli on riittävän hyvä?

18.4.2017, 13:26 in Sovelluskehitys, Yleinen by Heikki Raatikainen

Maaliskuun alussa tuli tuotantoon uusin C#-versio yhdessä Visual Studion 2017:n kanssa. Vieläkö kieleen mahtuu uusia ominaisuuksia? No toki hyvääkin voi parantaa. Mutta mitenkä ne aikaisemmat versiomuutokset menikään… Ehdottomasti tärkeimmät versio ovat olleet C#2 (generics, partial class ja nullable) ja C#3 (lambda, extension methods, LINQ, var, anonymous types), joiden piirteitä käytetään jatkuvasti käytännössä jokaisessa sovelluksessa. Toki noissa versioissa oli myös piirteitä, jotka eivät ole muuttaneet koodaustapoja, esim. co- ja contravarianssi. Nelosversiossa tuli parametrien välitykseen helpottavia piirteitä ja dynamic-tietotyyppi, viitosessa taas async/await oli isoin muutos.

C#6:ssa on pitkä luettelo uusia ominaisuuksia, näistä ehdottomasti käyttökelpoisin ja eniten käytetty ominaisuus on string-interpolaatio ja Elvis-operaattori sitten toisena. Kertauksena todettakoon että string-interpolaatio korvaa string.Format -metodin, esimerkiksi

string s = $"indeksi: {indeksi}, nimi: {nimi}";

ja Elvis-operaattori:

int? pituus = nimi?.Length;

Muutenkin versio 6 painottui enemmän koodin lyhentämiseen kuin muuhun. Jos joku ei vielä tiedä niin Elvis-operaattorin virallinen nimi on Null conditional operator. Nimi tulee siitä, että sopivalla fontilla ?. näyttää Elviksen otsaletiltä ja silmiltä.
C# 7:n uusien ominaisuuksien lista näytti aluksi tylsältä. Teoriatasoon tarkemmin tutustuneena väittäisin kuitenkin, että muutokset ovat isompia ja merkittävämpiä kuin edellisessä versiossa. Poimin tähän muutaman uuden ominaisuuden, joiden uskon olevan hyödyllisiä parannuksia koodaajalle.

Dekonstruktio ja Tuple

Konstruktorin lisäksi voidaan luokkaan tehdä dekonstruktori. Se on metodi, joka palauttaa arvoja out-parametreillä. Esimerkki:

class Tuote
{
    public int Tuotenumero { get; set; }
    public string Nimi { get; set; }
    public decimal Hinta { get; set; }

    public void Deconstruct(out int tuotenumero, out string nimi, out decimal hinta)
    {
        tuotenumero = Tuotenumero;
        nimi = Nimi;
        hinta = Hinta;
    }
}

Metodia Deconstruct pystyy kutsumaan aivan kun mitä tahansa metodia, jolla on out-parametrejä. Uusi syntaksi mahdollistaa lyhennysmerkinnän:

Tuote t = new Tuote() { Tuotenumero = 1, Nimi = "Eka", Hinta = 10M };
var (tuotenumero, nimi, hinta) = t;
var (a, b, c) = t;
// edellinen on sama kuin:
t.Deconstruct(out a, out b, out c);

Tuote-luokan instantiointia seuraavat rivit kutsuvat siis dekonstruktoria. Kannattaa huomata, että aluksi omituisen näköinen syntaksi on lopulta ihan järkeen käypä, kun käy tarkistamassa kääntäjän tuottamasta IL-koodista miten kääntäjä tuon toteuttaa. Aivan samoin Extension Methods -tekniikka on yksinkertainen, mutta erityisen toimiva pieni temppu käännöksen aikana.
Tuple-luokka on ollut pitkään Frameworkissä, mutta kielen tasolla sille ei ole ollut kovin hyvää tukea ja osin siksi se on jäänyt käyttämättä. Nyt Tuple (tai tuplen kaltainen) tietorakenne on olemassa hieman kätevämmässä muodossa. Esimerkki:

var x = (1, "kaks");
int i = x.Item1; // kentät Item1, Item2, jne
(int id, string nimi) tuote = (1, "kakkonen"); // kentät id ja nimi
string tulos = $"Nimi: {tuote.nimi}";

Jos tuplen kenttiä ei ole erikseen nimetty, tulee niistä Item1, Item2, jne -nimisiä. Esimerkin kolmannella rivillä tupleen tulee nimetyt kentät id ja nimi, joten käyttäminen on helpompaa varsinkin, kun Intellisense tunnistaa kenttien nimet.
Tuplea voi käyttää myös paluuarvona:

private (int nro, string selite) pari(int i, string s)
{
    return (i, s);
}

ja kutsu:

var ret = pari(33, "33");
tulos = $"{ret.nro} - {ret.selite}";

Uskoisin, että nyt tuple on käyttökelpoinen ja sille löytyy enemmän käyttötarkoituksia paikallisten luokkien toteutukseen tai tiedonsiirtoon luokkien välillä. Tämä vaatii FX 4.7-version. Jos käytössä on 4.6.2, pitää projektiin lisätä tarvittavat ominaisuudet NuGet-paketilla: Install-Package ”System.ValueTuple”.
Numeroliteraalit

Pari pientä muutosta on tullut numeroarvojen esittämiseen: binääriluvut ja erotinmerkki. Nyt voidaan siis heksalukujen (0x) lisäksi käyttää binäärilukuja (0b). Alaviivaa voi käyttää erotinmerkkinä kaikissa numerotyypeissä luettavuuden parantamiseksi.

int a = 0b1010;
int ff = 0b1111_1111;

Mitä muuta?

Muista uusista piirteistä lisää seuraavassa blogissa. Tähän loppuun vielä yksi pieni mutta kätevä muutos. Out-parametrimuuttujan voi esitellä inline eli metodin kutsussa:

int.TryParse(txtLukumäärä.Text, out int lkm);

Enää ei tarvitse erikseen esitellä muuttujaa ennen metodin kutsumista – yksinkertainen muutos, joka ei mullista koodausta mutta tulee varmasti käyttöön.

Alkuperäiseen otsikon kysymykseen palatakseni: tuskin milloinkaan.