C sharp

ohjelmointikieli From Wikipedia, the free encyclopedia

C sharp
Remove ads

C# (lausutaan ”si sharp”) on Microsoftin kehittämä yleiskäyttöinen, korkean tason ohjelmointikieli, joka tukee useita ohjelmointiparadigmoja, kuten oliopohjaista, imperatiivista, funktionaalista, geneeristä ja komponenttipohjaista ohjelmointia.[2][3] Kieli on olennainen osa .NET-ekosysteemiä ja sitä käytetään laajasti sekä Microsoftin omissa että avoimen lähdekoodin toteutuksissa, kuten .NET-, Mono- ja Unity-ympäristöissä.[2]

Pikafaktoja
Teknisten rajoitusten vuoksi artikkelin yllä näkyvä otsikko on virheellisessä muodossa. Oikea kirjoitustapa on: C#.

Kielessä on staattinen ja vahva tyypitys, automaattinen muistinhallinta roskienkeruun avulla sekä suoritusmalli, jossa lähdekoodi käännetään ensin välitasokoodiksi (Common Intermediate Language, CIL) ja suoritetaan sen jälkeen tyypillisesti ajonaikaisen kääntämisen (just-in-time, JIT) tai ennen ajoa tehtävän kääntämisen (ahead-of-time, AOT) kautta.[3] Kieli on suunniteltu moderniksi ja turvalliseksi vaihtoehdoksi C-suvun kielille, säilyttäen samalla monia C++- ja Java-kielistä tuttuja syntaksisia elementtejä.[4]

C#:llä kehitetään muun muassa yrityssovelluksia, verkkosovelluksia ja -palveluita, pilvipohjaisia ratkaisuja (Microsoft Azure), työpöytä- ja mobiilisovelluksia (.NET MAUI), pelejä (Unity), selainpohjaisia sovelluksia (Blazor) sekä data- ja tekoälysovelluksia (ML.NET).[2] Uusin vakaa versio on C# 14.0, joka julkaistiin marraskuussa 2025 osana .NET 10 -alustaa.[1]

Remove ads

Historia

Tausta ja synty

1990-luvun lopussa Microsoft aloitti .NET-hankkeen yhtenäisen sovelluskehitysalustan luomiseksi. Hankkeen puitteissa aloitettiin uuden C#-ohjelmointikielen kehitys pääsuunnittelija Anders Hejlsbergin johdolla.[4] Kieli esiteltiin julkisesti vuonna 2000 osana .NET Framework -alustaa ja Visual Studio .NET -kehitysympäristöä.[2]

Microsoft toimitti kielen ja siihen liittyvän Common Language Infrastructure (CLI) -spesifikaation standardoitavaksi Ecma Internationalille. Kielestä tuli Ecma-standardi ECMA-334 joulukuussa 2002 ja myöhemmin ISO/IEC-standardi ISO/IEC 23270.[3] Standardointi helpotti vaihtoehtoisten toteutusten, kuten avoimen lähdekoodin Mono-projektin, kehittämistä.[2]

Kielen nimi ”C#” viittaa ohjelmointikielten C-sukuun ja musiikin korotettuun säveleen (C♯). Virallisissa yhteyksissä nimi kirjoitetaan risuaitamerkin (#) kanssa ECMA-standardin mukaisesti (”C#”).[4]

Varhaiset versiot (1.0–4.0)

Ensimmäinen C#-versio (1.0) julkaistiin vuonna 2002 osana .NET Framework 1.0 -alustaa ja Visual Studio .NET -kehitysympäristöä.[5] Versio tarjosi oliopohjaiset perusrakenteet, kuten luokat, rakenteet, rajapinnat, poikkeuskäsittelyn, ominaisuudet, tapahtumat ja delegaatit, sekä erottelun viite- ja arvotyyppien välillä.[4] Kieli perustui automaattiseen roskienkeruuseen Common Language Runtime (CLR) -ympäristössä.

C# 2.0 julkaistiin vuonna 2005 .NET Framework 2.0 -version yhteydessä. Versiossa esiteltiin geneeriset tyypit ja menetelmät, nullable-arvotyypit, anonyymit metodit, iteraattorit (yield return), osittaiset tyypit sekä staattiset luokat.[6] Geneerisyys toteutettiin CLR-tasolla ilman tyyppien poistoa (type erasure), mikä paransi suorituskykyä ja tyyppiturvallisuutta.

C# 3.0 julkaistiin vuonna 2007 .NET Framework 3.5 -alustan mukana. Versio keskittyi kielen ilmaisukyvyn parantamiseen ja LINQ-kyselyjen tukemiseen. Uusia ominaisuuksia olivat lambda-lausekkeet, paikallisen tyypin päättely (var), anonyymit tyypit, laajennusmetodit, kyselylausekerakenne sekä ilmaisupuut (expression trees).[7]

C# 4.0 julkaistiin vuonna 2010 .NET Framework 4 -alustan yhteydessä. Versio toi dynaamisen sidonnan (dynamic), nimetyt ja valinnaiset parametrit sekä parannetun kovarianssin ja kontravarianssin geneerisille rajapinnoille ja delegaateille.[8] Ominaisuudet helpottivat yhteentoimivuutta dynaamisten kielten ja COM-komponenttien kanssa.

.NET Core ja avoimen lähdekoodin vaihe

Vuosina 2012–2015 julkaistut C# 5.0 ja C# 6.0 keskittyivät kielen ilmaisukyvyn ja käytettävyyden parantamiseen. C# 5.0 (2012) toi asynkronisen ohjelmoinnin async/await-rakenteet.[9] C# 6.0 (2015) esitteli useita käytännön parannuksia, kuten merkkijonointerpolaation, staattiset using-lausekkeet, lausekemuotoiset jäsenet, null-ehtoisen operaattorin parannukset sekä suodatellun poikkeuskäsittelyn.[10]

Vuonna 2014 Microsoft avasi C#:n kääntäjäalustan Roslyn lähdekoodin ja siirsi kehityksen GitHubiin .NET Foundation -säätiön alaisuuteen.[11] Samalla käynnistettiin avoimen lähdekoodin .NET Core -alusta, jonka ensimmäinen versio julkaistiin vuonna 2016. Se mahdollisti C#-sovellusten ajamisen Windowsin ohella myös Linux- ja macOS-käyttöjärjestelmissä.[12] Avoimuus laajensi merkittävästi kehittäjäyhteisön osallistumista kielen ja alustan kehitykseen.

C# 7.0 -sarja (2017–2019) sidottiin tiiviisti .NET Core -julkaisusykliin. Versiot toivat muun muassa arvotuuplit, parannetun mallinsovituksen, paikalliset funktiot, ref-palautukset ja suorituskykyä parantavia rakenteita kuten Span<T> sekä ref struct.[13][14] C# 8.0 (2019) esitteli nullable-viitetyypit, oletustoteutukset rajapinnoille, asynkroniset virrat sekä indices and ranges -syntaksit.[15] Nämä ominaisuudet vahvistivat kielen asemaa turvallisessa ja monialustaisessa sovelluskehityksessä.

Yhtenäinen .NET-alusta (5+) ja uusimmat kehitysaskeleet

Vuodesta 2020 alkaen Microsoft yhdisti aiemmat .NET Framework-, .NET Core- ja Xamarin/Mono-haarat yhtenäiseen .NET-alustaan. .NET 5 julkaistiin vuonna 2020, ja sitä seurasivat .NET 6 (2021), .NET 7 (2022), .NET 8 (2023), .NET 9 (2024) ja .NET 10 (2025).[16] Julkaisut noudattavat vuosittaista rytmiä, jossa parilliset .NET-versiot ovat pitkäaikaisen tuen (Long-Term Support, LTS) julkaisuja ja parittomat lyhyemmän tuen (Standard Term Support, STS) julkaisuja.[17]

C# 9.0 julkaistiin .NET 5:n yhteydessä vuonna 2020. Versio toi record-tyypit, init only -ominaisuudet, ylä­tason ohjelmat sekä parannuksia mallinsovitukseen.[18] C# 10.0 (2021) esitteli globaalit using-lauselmat, tiedostokohtaiset nimiavaruudet, record struct -tyypit sekä parannuksia lambda-lausekkeisiin.[19]

C# 11.0 (2022) toi raa'at merkkijonoliteraalit, listamallit, geneeriset attribuutit ja required-jäsenet.[20] C# 12.0 (2023) laajensi pääkonstruktorit luokkiin ja rakenteisiin, lisäsi kokoelmailmaisut sekä paransi lambda-parametrien oletusarvoja ja geneeristä ohjelmointia.[21]

C# 13.0 (2024) ja C# 14.0 (2025) julkaistiin .NET 9- ja .NET 10 -alustojen mukana. Versiot toivat parannuksia muun muassa params-kokoelmiin, lock-objektiin, ref struct -rajapintoihin sekä tyyppien laajentamiseen ilman perintää (extension members).[22][1] Kielen kehitys jatkuu vuosittaisena ja avoimena prosessina .NET-alustan rinnalla.[23]

Remove ads

Suunnittelutavoitteet ja vaikutteet

Suunnittelutavoitteet

C# suunniteltiin yleiskäyttöiseksi, moderniksi ohjelmointikieleksi, joka tukee useita ohjelmointiparadigmoja mutta säilyttää C-suvun kielille tyypillisen syntaksin.[4] Kielen keskeisiin tavoitteisiin kuuluivat vahva staattinen tyyppijärjestelmä, automaattinen roskienkeruu sekä tuki turvalliselle ja ylläpidettävälle sovelluskehitykselle.[3] C# suunniteltiin tarjoamaan C++-kielen suorituskykyä muistuttava teho yhdistettynä korkean tason kielen kaltaiseen kehitysmukavuuteen.[24]

C# on tiiviisti sidottu Common Language Infrastructure (CLI) ja Common Language Runtime (CLR) -ympäristöihin. Kielen suunnittelutavoitteisiin kuuluu, että C#:llä kirjoitetut ohjelmat käännetään ECMA-334-määrittelyn mukaan yhteiseen välitasokoodiin Common Intermediate Language (CIL), jota voivat jakaa useat eri CLI-yhteensopivat kielet. Tavoitteena on tukea kielien välistä yhteentoimivuutta eri CLI-yhteensopivien kielten, kuten Visual Basic .NETin ja muiden .NET-kielten, välillä, sekä vahvaa tyyppiturvallisuutta, versioitavuutta ja komponenttipohjaista sovelluskehitystä.[3][2]

Kielen suunnittelussa otettiin huomioon sekä järjestelmäohjelmoinnin että sovelluskehityksen tarpeet. C# tarjoaa korkean tason ominaisuuksia, kuten poikkeuskäsittelyn, automaattisen muistinhallinnan ja laajan luokkakirjaston, mutta mahdollistaa myös matalamman tason ohjelmoinnin esimerkiksi osoittimia hyödyntävän unsafe-tilan kautta silloin, kun halutaan minimoida roskienkeruun aiheuttamia viiveitä tai integroitua olemassa oleviin rajapintoihin.[3]

Vaikutteet muista kielistä

C# kuuluu syntaksiltaan C-sukuisiin kieliin, ja sen perusrakenne – lohkot, ohjausrakenteet, tyyppimääritykset ja operaattorit – muistuttavat läheisesti C++- ja Java-kieliä.[4] Tämä helpottaa siirtymistä aiemmista C-tyyppisistä kielistä C#:ään, ja mahdollistaa olemassa olevien ohjelmointitapojen hyödyntämisen .NET-ympäristössä. C#:n olio-malli, rajapinnat ja poikkeuskäsittely noudattavat pitkälti samoja periaatteita kuin muissa modernissa olio-ohjelmointikielissä.

Kielen suunnitteluun vaikuttivat myös komponenttipohjaiset kielet ja ympäristöt, kuten Visual Basic ja Delphi, joissa korostuvat ominaisuudet, tapahtumat ja versioitavat kirjastot.[24] C#:ssä nämä ideat yhdistyvät CLR:n metatietomalliin ja kokoonpanoihin (assemblies), mikä tukee komponenttipohjaista ohjelmistokehitystä, uudelleenkäytettävyyttä ja versiohallittuja kirjastoja.

Lisäksi C#:n kehityksessä on omaksuttu ideoita mm. funktionaalisesta ohjelmoinnista, mikä näkyy esimerkiksi lambda-lausekkeiden, LINQ-kyselyjen ja mallinsovituksen kaltaisina ominaisuuksina. Kielen myöhemmät versiot ovat tuoneet lisää funktionaalisia piirteitä, kuten immuuttisuutta korostavia record-tyyppejä ja ilmaisupuita, säilyttäen samalla taaksepäin yhteensopivuuden alkuperäisen C-tyylisen syntaksin kanssa.[25]

Remove ads

Tyyppijärjestelmä ja muistinhallinta

Tyyppijärjestelmä

C#:ssä on staattinen ja vahva tyyppijärjestelmä, joka perustuu CLI:n yhteiseen tyyppijärjestelmään (Common Type System, CTS). Kaikki tyypit – sekä kielen sisäänrakennetut että käyttäjän määrittelemät – jakautuvat kahteen pääryhmään: arvotyyppeihin (esimerkiksi peruslukutyypit, rakenteet ja enumeraatiot) ja viitetyyppeihin (esimerkiksi luokat, rajapinnat, taulukot ja delegaatit.[26] Tyyppijärjestelmä on nimellinen ja suunniteltu siten, että kääntäjä ja CLR voivat tarkistaa tyypinmukaisuuden ja estää suuren osan ajoaikaisista tyyppivirheistä jo käännösvaiheessa.

Arvotyypit tallennetaan tyypillisesti suoraan muuttujan muistipaikkaan (esimerkiksi pinossa tai osana toista rakennetta), kun taas viitetyypeistä talletetaan viite kekoon varattuun olioon. Kaikki tyypit voidaan käsitellä object-perustyypin kautta, mikä mahdollistaa yhtenäisen käsittelyn ja esimerkiksi kokoelmiin tallentamisen. Arvotyypin pakkaaminen (boxing) muuntaa sen viitetyypiksi ja purkaminen (unboxing) takaisin arvotyypiksi; tämä on osa CTS-määrittelyä ja vaikuttaa sekä suorituskykyyn että muistinkäyttöön.

Kieli tukee laajaa joukkoa generisia kokoelmia ja geneeristä ohjelmointia, jossa tyyppiparametrit välitetään suoraan CLR-tason generisyytenä ilman tyyppien poistoa (type erasure). Tämä parantaa suorituskykyä ja vähentää pakkausoperaatioiden tarvetta verrattuna ei-geneerisiin rakenteisiin. Lisäksi C#:n tyyppipäättely (esimerkiksi var-avainsanan ja lambda-lausekkeiden yhteydessä) helpottaa koodin kirjoittamista muuttamatta tyyppijärjestelmän staattista luonnetta.

C#:n null-käsittely perustuu sekä arvotyyppien nullable-laajennoksiin (T? enumeraatioille ja muille arvotyypeille) että nullable-viitetyyppeihin, jotka otettiin käyttöön C# 8.0 -versiossa. Null-viitetyypit yhdistävät käännösaikaisen analyysin ja annotaatiot (esimerkiksi string? ja string) vähentääkseen null-viitevirheiden todennäköisyyttä tuotantokoodissa.[27]

Muistinhallinta

C#:ssa muistinhallinta on pääosin automaattista ja perustuu roskienkeruuseen CLR-ympäristössä. CLR hallinnoi viitetyyppien elinkaarta sijoittamalla ne hallittuun kekoon ja keräämällä käyttämättömät oliot pois generaatioperusteisen roskienkeruun avulla.[28] Tämä vähentää muistivuotojen riskiä ja helpottaa sovelluskehitystä verrattuna manuaaliseen muistinhallintaan, mutta tekee olioiden tuhoamisesta ei-determinististä: ohjelmoija ei voi tarkasti kontrolloida, milloin yksittäinen olio vapautetaan.

Resurssien hallintaa varten C# tarjoaa deterministisen käyttömallin IDisposable-rajapinnan ja using-rakenteen kautta. Mallin ideana on, että hallitun olion taustalla oleva järjestelmäresurssi (esimerkiksi tiedostokahva, verkkoyhteys tai tietokantayhteys) vapautetaan viipymättä, vaikka itse olio jäisi roskienkeruun varaan.[29] Tarvittaessa luokka voi määritellä myös finalisaattorin (finalizer), jonka CLR suorittaa ennen olion lopullista poistamista, mutta tätä pidetään yleensä viimeisenä varotoimena eikä ensisijaisena resurssinhallintamekanismina.

Vaikka C# on suunniteltu korkeantason ja turvalliseksi kieleksi, se tarjoaa myös mahdollisuuden hallitumpaan järjestelmäohjelmointiin ”unsafe”-tilan avulla. unsafe-koodissa voidaan käyttää osoittimia ja esimerkiksi stackalloc-operaattoria pinokohtaiseen muistiallokointiin, mikä antaa kokeneille kehittäjille mahdollisuuden optimoida suorituskykykriittisiä osia tai integroida olemassa oleviin natiiveihin rajapintoihin.Unsafe code, pointers, and function pointers Microsoft. Viitattu 7.12.2025. Uudemmat ominaisuudet, kuten Span<T> ja ref struct -tyypit, laajentavat tätä kykyä tarjoamalla muistiturvallisempia tapoja käsitellä viipaleita hallitusta tai hallitsemattomasta muistista ilman ylimääräisiä kopioita.[30]

Remove ads

Oliopohjaisuus ja perintä

Oliopohjaisuus

C# tukee täysimääräisesti olio-ohjelmoinnin perusperiaatteita: abstraktiota, kapselointia, perintää ja polymorfismia. Kielen oliomalli rakentuu luokkien (class) ja rakenteiden (struct) ympärille. Kaikki C#:n viitetyypit perivät lopulta object-perustyypistä, mikä tarjoaa yhtenäisen joukon perittyjä jäseniä (esimerkiksi ToString(), Equals() ja GetHashCode()) kaikille olioille.[31][32] Oliot kapseloivat tilansa ja toimintonsa muokkausrajoitteiden ja pääsynvalvontamääreiden (public, internal, protected, private) avulla.

Perintä

Perintä C#:ssä perustuu yksinkertaiseen luokkaperintään: jokaisella luokalla voi olla vain yksi suora kantaluokka (base class). C#-kieli ei aseta rajoituksia perintähierarkian syvyydelle.Johdettu luokka määritellään kaksoispiste-syntaksilla (class Derived : Base), ja se voi käyttää kantaluokan jäseniä sekä laajentaa tai muokata niiden toiminnallisuutta.[33] Rakenteet (struct) ovat arvotyyppejä, eivätkä ne voi periä toisista rakenteista tai luokista, mutta ne voivat toteuttaa rajapintoja.[32]

Polymorfismi toteutetaan C#:ssä virtuaalisten jäsenten ja ylikirjoituksen avulla. Kantaluokan jäsen voidaan merkitä virtuaaliseksi avainsanalla virtual tai abstraktiksi avainsanalla abstract, jolloin johdetut luokat voivat määritellä oman toteutuksensa avainsanalla override. Näin olion todellinen tyyppi ratkaisee ajoaikaisesti, mitä metodia kutsutaan, vaikka viitteen tyyppi olisi kantaluokka.[34] Luokka tai jäsen voidaan puolestaan merkitä avainsanalla sealed estämään jatkoperintä ja ylikirjoittaminen, jolloin tyyppi tai jäsen lukitaan nykyiseen muotoonsa.[35]

Usean kantaluokan perinnän sijaan C#:ssä käytetään rajapintoja (interface) tyypillisesti ”sopimusten” kuvaamiseen. Luokka tai rakenne voi toteuttaa useita rajapintoja samanaikaisesti (class Sample : IFoo, IBar), jolloin se sitoutuu tarjoamaan rajapintojen määrittelemät jäsenet. Rajapinnat tukevat myös polymorfismia, sillä rajapinnan kautta käsitelty olio voidaan korvata millä tahansa toteutustyypillä, joka toteuttaa kyseisen rajapinnan. C# 8.0 toi rajapinnoille mahdollisuuden määritellä oletustoteutuksia (default interface methods), joiden avulla rajapintaan voidaan lisätä uusia jäseniä rikkomatta aiempia toteutuksia; toteuttava tyyppi voi halutessaan ylikirjoittaa oletuskäyttäytymisen.[36]

C# 9.0 -versiosta alkaen kieleen lisättiin record-tyypit, jotka tarjoavat oliopohjaisen mutta arvosemanttisen mallin erityisesti tietomalleja varten. record class on viitetyyppi, joka tukee perintää muiden record-tyyppien kanssa ja määrittelee oletuksena arvoon perustuvan vertailun, kun taas record struct on arvotyyppi vastaavalla syntaksilla.[37] Record-tyypit täydentävät perinteistä luokkaperintää tarjoamalla välineen immuuttien ja arvovertailua käyttävien olioiden määrittelyyn samalla, kun ne integroituvat C#:n olemassa olevaan perintä- ja polymorfismimalliin.

Remove ads

Delegaatit, tapahtumat ja lambda-lausekkeet

Delegaatit

C#:n delegaatit ovat tyyppejä, jotka edustavat viitteitä metodeihin, joilla on tietty parametri­lista ja paluuarvon tyyppi. Delegaatin ilmentymä kapseloi yhden tai useamman kutsuttavan yksikön (invocation list), ja sitä voidaan kutsua aivan kuin tavallista metodia. Delegaatit perustuvat System.Delegate-pohjaluokkaan, ja niiden avulla metodeja voidaan välittää argumentteina, tallettaa muuttujiksi tai rakentaa irrotettuja suunnittelumalleja kuten havaitsijamalli.[38][39] Delegaatteja on sekä ei-geneerisiä että geneerisiä versioita (esimerkiksi Action- ja Func-delegaatit). Geneeriset delegaatit tukevat kovarianssia ja kontravarianssia yhteensopivien allekirjoitusten välillä.[40]

Useimmat C#:ssa käytettävät delegaatit ovat monilähetysdelegaatteja (multicast delegates), joiden kutsulista voi sisältää useita metodeja. Kun delegaattia kutsutaan, sen kutsulistan kaikki jäsenet suoritetaan järjestyksessä. Tätä hyödynnetään esimerkiksi tapahtumankäsittelyssä, jossa samaan tapahtumaan voidaan liittää useita käsittelijöitä (event handlers).[39]

Tapahtumat

Tapahtumat (events) rakentuvat delegaattien varaan ja tarjoavat vakiintuneen mallin, jossa yksi luokka (julkaisija) voi ilmoittaa kiinnostavista tilanmuutoksista muille luokille (tilaajat). Tapahtuma määritellään event-avainsanalla ja liitetään tiettyyn delegaattityyppiin; tilaajat rekisteröivät käsittelijänsä käyttämällä +=-operaattoria ja poistavat ne −=-operaattorilla. Vain tapahtuman määritellyt luokka voi ”laukaista” (raise) tapahtuman, mikä auttaa rajaamaan sen elinkaarta ja ylläpitämään kapselointia.[41] Yleinen käytäntö on hyödyntää EventHandler- ja EventHandler<TEventArgs>-delegaatteja sekä EventArgs-pohjaisia tietoluokkia, jolloin tapahtumien allekirjoitukset pysyvät yhdenmukaisina .NET-ekosysteemissä.[42]

Lambda-lausekkeet

Lambda-lausekkeet tarjoavat tiiviin syntaksin anonyymien funktioiden määrittelyyn ja ovat keskeinen osa C#:n funktionaalisia piirteitä. Lambda-lauseke määritellään =>-operaattorin avulla, joka erottaa parametrit ja lausekkeen tai koodilohkon (esimerkiksi x => x * x). Lambda voi olla joko lausekelambda (expression lambda) tai lausekelohkoa käyttävä lauselambda (statement lambda).[43][44] Lambda-lausekkeet voidaan muuntaa sekä delegaatti-ilmentymiksi että ilmaisupuiksi, mitä hyödynnetään muun muassa LINQ-tekniikoissa ja dynaamisissa kyselykirjastoissa.

Kielen tasolla lambda-lausekkeet ovat syntaktinen lyhennys anonyymeille delegaateille, ja niitä voi käyttää kaikissa yhteyksissä, joissa odotetaan delegaattityyppiä tai sopivaa geneeristä funktiotyyppiä. Lambda-lausekkeet voivat muodostaa sulkeumia (closures), jolloin ne säilyttävät viittaukset ympäröivän koodin paikallisiin muuttujiin tai instanssitilaan myöhempiä takaisinkutsuja varten.[43] Myöhemmissä C#:n versioissa lambda-lausekkeita on laajennettu muun muassa luonnollisen tyypin päättelyllä, attribuuttien ja oletusparametrien tuella sekä ilmaisumuotoisilla jäsenillä (expression-bodied members), mikä on tehnyt niistä keskeisen osan nykyaikaista C#-ohjelmointia.[45]

Remove ads

Geneerisyys

C#:n geneerisyys otettiin käyttöön C# 2.0 -versiossa osana .NET Framework 2.0 -alustaa. Geneeristen tyyppien ja menetelmien tavoitteena on parantaa tyyppiturvallisuutta ja suorituskykyä verrattuna ei-geneerisiin kokoelmiin ja yleiskäyttöisiin viitepohjaisiin ratkaisuihin. Geneeriset tyypit ja metodit määritellään tyyppiparametrien (esimerkiksi List<T>, Dictionary<TKey,TValue> tai void Swap<T>(ref T x, ref T y)) avulla, ja niitä voidaan käyttää sekä luokissa, rakenteissa, rajapinnoissa että delegaateissa.[46][47]

C#:n geneerisyys on toteutettu CLR-tasolla reifioituina geneerisinä tyyppeinä, mikä tarkoittaa, että tyyppiparametrien konkreettinen tyyppi säilyy ajon aikana ilman tyyppien poistoa (type erasure). Tämä mahdollistaa sen, että geneeriset kokoelmat, kuten List<T>, voivat säilyttää arvotyyppejä ilman pakkausta (boxing), mikä vähentää sekä ylimääräistä muistinkäyttöä että ajoaikaista kuormaa verrattuna ei-geneerisiin kokoelmiin (esimerkiksi System.Collections.ArrayList).[48] Samalla geneerinen tyyppijärjestelmä mahdollistaa täyden käännöaikaisen tarkistuksen ilman heijastukseen perustuvaa dynamiikkaa.

Rajoitteet ja tyyppiparametrit

Geneerisiä tyyppiparametreja voidaan rajoittaa rajoitteilla (constraints), jotka määritellään where-lausekkeiden avulla. Rajoitteiden avulla voidaan esimerkiksi edellyttää, että tyyppiparametri on viitetyyppi (where T : class), arvotyyppi (where T : struct), tiettyä rajapintaa tai kantaluokkaa toteuttava tyyppi (where T : IDisposable tai where T : BaseClass) tai tyyppi, jolla on julkinen parametriton konstruktori (where T : new()). Myöhemmissä versioissa (alkaen C# 7.3) on lisätty uusia rajoitteita, kuten unmanaged-rajoite matalan tason muistirakenteita varten sekä useiden rajoitteiden yhdistäminen samaan tyyppiparametriin.[49] Rajoitteet parantavat geneeristen API-rajapintojen ilmaisukykyä ja helpottavat staattista analyysiä.

Kovarianssi ja kontravarianssi

Kovarianssi ja kontravarianssi mahdollistaa yhteensopivuuden eritasoisten tyyppihierarkioiden kanssa ilman ylimääräisiä tyyppimuunnoksia. Esimerkiksi IEnumerable<out T> on kovariantti tyyppiparametrinsa suhteen, jolloin IEnumerable<Derived> voidaan käsitellä IEnumerable<Base>-tyyppinä, kun Derived perii Base-tyypistä.[50] Vastaavasti kontravarianttiset delegaatit ja rajapinnat voivat vastaanottaa laajemman tyyppijoukon argumentteja, mikä helpottaa suunnittelumallien ja irrotettujen API-rajapintojen toteuttamista.

Geneerisyys on laajentunut myöhemmissä C#:n versioissa uusilla ominaisuuksilla, kuten geneerisillä attribuuteilla (generic attributes) ja parannetuilla päättelysäännöillä tyyppiparametreille. C# 11.0 toi mahdollisuuden määritellä attribuutteja, joilla on tyyppiparametreja (esimerkiksi [MyAttribute<T>]) ja joita voidaan käyttää geneeristen API-rajapintojen ja metatietojen mallintamiseen aiempaa täsmällisemmin.[51] Geneerisyys muodostaa myös perustan monille muille kielen ominaisuuksille, kuten LINQ-kirjastoille, asynkroniselle ohjelmoinnille (Task<TResult>) ja lukuisille .NET-ekosysteemin kokoelmille ja apuluokille.

Remove ads

LINQ

LINQ (Language Integrated Query) on C#:n kyselyominaisuus, joka otettiin käyttöön C# 3.0 -versiossa vuonna 2007 osana .NET Framework 3.5 -alustaa. LINQ mahdollistaa yhtenäisen, tyyppiturvallisen ja käännösaikaisesti tarkistetun tavan suorittaa kyselyitä erilaisiin tietolähteisiin suoraan C#-koodissa ilman erillisiä kyselykieliä tai merkkijonojen käsin muodostamista. Ominaisuus rakentuu useiden C# 3.0:n uusien kielielementtien varaan: lambda-lausekkeisiin, laajennusmetodeihin, anonyymeihin tyyppeihin, paikalliseen tyyppipäättelyyn (var), ilmaisupuihin sekä osittaisiin metodeihin.[52][53]

Syntaksi

LINQ tukee kahta rinnakkaista syntaksia: kyselysyntaksia (query syntax), joka muistuttaa SQL:ää, sekä metodisyntaksia (method syntax), joka käyttää ketjutettuja laajennusmetodikutsuja. Kyselysyntaksi käännetään kääntäjän toimesta metodisyntaksiksi, joten molemmat ovat semanttisesti identtisiä. Tyypillisiä LINQ-operaattoreita ovat muun muassa Select, Where, OrderBy, GroupBy, Join ja Aggregate. Operaattorit on toteutettu geneerisinä laajennusmetodeina System.Linq.Enumerable- ja System.Linq.Queryable-luokkiin, jotka toimivat kaikilla tyypeillä, jotka toteuttavat IEnumerable<T>- tai IQueryable<T>-rajapinnan.[54]

Ilmaisupuut ja providerit

Keskeinen LINQ:n arkkitehtuurinen piirre on ilmaisupuut (expression trees), jotka mahdollistavat kyselyjen esittämisen tietorakenteina suoritettavan koodin sijaan. Kun kysely kohdistuu IQueryable<T>-tyyppiin, kääntäjä muodostaa ilmaisupuusta esityksen, joka välitetään providerille; provider voi muuntaa sen esimerkiksi SQL-kyselyksi tietokantaa varten tai muuksi kohdejärjestelmän ymmärtämäksi muodoksi. Tunnettuja LINQ-providereita ovat LINQ to Objects (paikallisille kokoelmille), LINQ to XML, LINQ to DataSet, LINQ to SQL sekä Entity Frameworkin tarjoama LINQ to Entities.[55] Myöhemmin kehitetyt rinnakkais- ja asynkroniset laajennukset, kuten PLINQ ja asynkroniset kyselyt (C# 8.0+), laajentavat LINQ:n käyttöä suorituskykykriittisiin ja I/O-painotteisiin skenaarioihin.[56]

LINQ on muodostunut yhdeksi C#:n eniten käytetyistä ominaisuuksista, ja sillä on keskeinen rooli nykyaikaisessa .NET-kehityksessä tietojen käsittelyssä, suodatuksessa ja muuntelussa. Se on vaikuttanut myös monien kolmannen osapuolen kirjastojen suunnitteluun ja toimii perustana esimerkiksi Reactive Extensions (Rx) -kirjastolle sekä useille data-analytiikka- ja raportointityökaluille.[57]

Remove ads

Asynkroninen ohjelmointi (async/await)

C#:n tuki asynkroniselle ohjelmoinnille perustuu async- ja await-avainsanoihin, jotka otettiin käyttöön C# 5.0 -versiossa vuonna 2012 osana .NET Framework 4.5 -alustaa. Ominaisuus mahdollistaa ei-blokkaavan koodin kirjoittamisen synkronisen kaltaisella rakenteella, mikä helpottaa erityisesti I/O-painotteisten operaatioiden, kuten tiedostojen lukemisen, verkkopyyntöjen ja tietokantakyselyiden käsittelyä ilman erillisiä säikeitä tai takaisinkutsuja.[58][59]

Perusteet

Metodi merkitään async-avainsanalla, jolloin se voi sisältää yhden tai useamman await-operaattorin. Await keskeyttää metodin suorituksen odottaessaan asynkronisen operaation valmistumista, mutta vapauttaa kutsuvan säikeen muihin tehtäviin.

Käytännössä malli rakentuu .NET:in säiepoolin ja käyttöjärjestelmän tarjoamien asynkronisten I/O-mekanismien varaan: säikeet toimivat edelleen suoritusyksikköinä, mutta niiden hallinta on kapseloitu Task- ja async/await-abstraktioiden taakse, mikä vähentää kehittäjän tarvetta käsitellä säikeitä suoraan.

Vaikka perinteinen System.Threading.Thread-luokka on yhä käytettävissä yksittäisten säikeiden luomiseen ja ohjaamiseen, nykyaikaisessa C#-koodissa suositaan yleensä Task- ja async/await-pohjaisia malleja säikeiden hallinnan yksinkertaistamiseksi. Paluutyyppinä käytetään tyypillisesti Task tai Task<T>, jotka edustavat asynkronista operaatiota ja sen mahdollista tulosta; erityistapauksena voidaan käyttää myös ValueTask-pohjaisia tyyppejä suorituskykykriittisissä skenaarioissa.[60][61] Kääntäjä muuntaa async-metodin tilakoneeksi (state machine), joka hallitsee jatkamisia sekä poikkeusten käsittelyä ja suoritus­kontekstin säilyttämistä.

Async/await-malli integroituu saumattomasti .NET-ekosysteemiin ja on muodostunut vakiintuneeksi tavaksi käsitellä asynkronisuutta esimerkiksi ASP.NET-verkkosovelluksissa, .NET MAUI -mobiilisovelluksissa ja pilvipalveluissa. Se vähentää säikeiden hallinnan monimutkaisuutta ja parantaa sovellusten skaalautuvuutta verrattuna perinteisiin säiemalleihin tai tapahtumapohjaiseen asynkronisuuteen.[62]

Asynkroniset virrat

C# 8.0 -versiossa käyttöön tulleet asynkroniset virrat (async streams) laajentavat mallia tukemaan IAsyncEnumerable<T>-rajapintaa. Tämä mahdollistaa tietojen tuottamisen ja kuluttamisen asynkronisesti await foreach -rakenteella, mikä soveltuu erityisesti reaaliaikaiseen datan käsittelyyn ja suurten tietomäärien striimaukseen ilman koko tuloksen lataamista muistiin kerralla.[63] Asynkroniset virrat täydentävät perinteistä Task-pohjaista mallia tarjoamalla luonnollisen tavan mallintaa sekvenssejä, joissa sekä yksittäisten alkioiden tuotto että koko sekvenssi ovat asynkronisia.

Remove ads

Mallinsovitus (pattern matching)

C#:n mallinsovitus (pattern matching) on kielirakenne, joka mahdollistaa arvojen ja olioiden rakenteen tarkistamisen sekä tietojen purkamisen ehdollisessa ja tiiviissä muodossa. Ominaisuus otettiin käyttöön C# 7.0 -versiossa vuonna 2017 ja sitä on laajennettu merkittävästi myöhemmissä versioissa.[64] Mallinsovitus korvaa ja laajentaa perinteisiä if- ja switch-lauseita tarjoamalla ilmaisuvoimaisemman tavan käsitellä monimuotoisia tietorakenteita, kuten olioiden tyyppejä, ominaisuuksia ja arvoja.[65]

Syntaksi

Mallinsovitusta käytetään ensisijaisesti is-lausekkeen ja switch-lauseen yhteydessä. is-lauseke tarkistaa, vastaako muuttuja tiettyä mallia, ja voi samalla purkaa arvon uuteen muuttujaan (esimerkiksi if (obj is string s)). switch-lause tukee mallipohjaista haarautumista, jossa kukin case määrittelee mallin ja valinnaisen ehdon (when-lauseke).[66] C# 8.0 toi switch-ilmaisun (switch expression), joka palauttaa arvon ja käyttää tiiviimpää nuolisyntaksia. Myöhemmät versiot ovat lisänneet relaatiomalleja (>, < jne.), loogisia malleja (and, or, not), sulkeita ryhmittelyyn sekä listamalleja (C# 11.0).[67]

Kehitys versioittain

C# 7.0 toi ensimmäiset mallinsovitusominaisuudet tyyppi- ja vakio­malleilla is-lausekkeessa ja switch-lauseessa. C# 8.0 laajensi mallinsovitusta ominaisuusmalleilla (property patterns) ja switch-ilmaisulla, jotka helpottavat olioiden purkamista ominaisuuksiensa perusteella.[68] C# 9.0 -versiosta alkaen mallinsovitus tukee relaatiomalleja ja loogisia yhdistelmiä suoraan is-lausekkeessa sekä yksinkertaistettua tyyppitarkistusta (esimerkiksi obj is not null). C# 10.0 paransi ominaisuusmalleja ja C# 11.0 lisäsi listamallit sekä viipalemallit (''slice patterns'', ''..''-operaattori), jotka soveltuvat erityisesti kokoelmien ja taulukoiden käsittelyyn.[69]

Mallinsovitus on muodostunut keskeiseksi osaksi modernia C#-ohjelmointia erityisesti tietomallien, syötteen validoinnin ja monimuotoisten tietorakenteiden käsittelyn yhteydessä. Se parantaa koodin luettavuutta ja turvallisuutta vähentäen manuaalisten tyyppitarkistusten, tyyppimuunnosten ja poikkeuskäsittelyn tarvetta.[70]

Remove ads

Muut keskeiset ominaisuudet

Nullable-viitetyypit

C# 8.0 -versiossa käyttöön otetut nullable-viitetyypit laajentavat tyyppijärjestelmää erottelemalla viitetyypit, jotka voivat olla null-arvoisia (esimerkiksi string?) ja jotka eivät voi (string). Kääntäjä suorittaa virta-analyysin (flow analysis) ja varoittaa mahdollisista null-viitepoikkeuksista jo käännösaikana. Ominaisuus on valinnainen ja otetaan käyttöön projektitasolla <Nullable>enable</Nullable>-asetuksella tai #nullable enable-direktiivillä.[71][72] Nullable-viitetyypit parantavat tuotantokoodin turvallisuutta ja ovat muodostuneet suosituksi käytännöksi uusissa .NET-projekteissa.

Record-tyypit

C# 9.0 toi record-tyypit, jotka tarjoavat tiiviin syntaksin tietojen kantamiseen tarkoitettujen tyyppien määrittelyyn. record class on viitetyyppi ja record struct arvotyyppi; molemmat tukevat oletuksena arvoon perustuvaa vertailua (value-based equality), with-ilmaisuja immuuttien kopioiden luomiseen sekä dekonstruktiota. Recordien pääkonstruktori (primary constructor) mahdollistaa ominaisuuksien määrittelyn suoraan tyypin otsikossa (esimerkiksi public record Person(string FirstName, string LastName);).[73][74] Recordit täydentävät perinteisiä luokkia erityisesti tietomallien ja DTO-objektien (data transfer object) yhteydessä.

Ylätason lauseet ja tiedostokohtaiset nimiavaruudet

C# 9.0 esitteli ylätason lauseet (top-level statements), joiden avulla ohjelman päälogiikka voidaan kirjoittaa suoraan tiedoston alkuun ilman eksplisiittistä Main-metodia tai luokkarakennetta. C# 10.0 toi tiedostokohtaiset nimiavaruudet (file-scoped namespaces), jotka määritellään yhden rivin syntaksilla ja kattavat koko tiedoston.[75][76] Nämä ominaisuudet yksinkertaistavat erityisesti pienten ohjelmien ja skriptien kirjoittamista sekä parantavat koodin luettavuutta suuremmissa projekteissa.

Pääkonstruktorit luokissa ja rakenteissa sekä kokoelmailmaisut

C# 12.0 laajensi pääkonstruktorit (primary constructors) koskemaan myös luokkia ja rakenteita aiemman record-rajoituksen sijaan. Samalla käyttöön tulivat kokoelmailmaisut (collection expressions), jotka mahdollistavat kokoelmien alustamisen tiiviillä syntaksilla (esimerkiksi int[] array = [1, 2, 3];).[77] Ominaisuudet vähentävät toistoa ja parantavat koodin luettavuutta erityisesti tietorakenteiden alustuksessa.

Uusimmat kehitysaskeleet (C# 13–14)

C# 13.0 ja C# 14.0 toivat muun muassa parannuksia params-kokoelmiin, lock-objektiin, ref struct -rajapintoihin sekä extension members -ominaisuuden, joka mahdollistaa tyyppien laajentamisen ilman perintää.[78][79] Kehitys jatkuu vuosittaisena syklinä painottaen suorituskykyä, turvallisuutta ja kehittäjäkokemusta.

Remove ads

Esimerkkiohjelmat

Hello, World!

C#-ohjelma tulostaa tekstin Hello, World! konsoliin. C# 9.0 -versiosta alkaen ohjelman voi kirjoittaa ylätason lauseilla (top-level statements) ilman erillistä Main-metodia:[80]

using System;

Console.WriteLine("Hello, World!");

Sama ohjelma voidaan kirjoittaa myös perinteisellä Main-metodilla:

using System;

class Program
{
    static void Main()
    {
        Console.WriteLine("Hello, World!");
    }
}

Perusluokka ja olio

Esimerkkiluokka, jolla on ominaisuuksia, konstruktori ja metodi, sekä olion luonti:

public class Person
{
    public string FirstName { get; }
    public string LastName  { get; }

    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName  = lastName;
    }

    public string GetFullName() => $"{FirstName} {LastName}";
}

// Käyttöesimerkki:
var person = new Person("Grace", "Hopper");
var name   = person.GetFullName(); // "Grace Hopper"

LINQ-kysely

Esimerkissä LINQ-kysely suodattaa taulukosta parilliset luvut ja lajittelee ne laskevaan järjestykseen.

using System;
using System.Linq;

int[] numbers = { 1, 2, 3, 4, 5, 6 };

var evens =
    from n in numbers
    where n % 2 == 0
    orderby n descending
    select n;

foreach (int n in evens)
{
    Console.WriteLine(n);
}
// Tuloste: 6, 4, 2

Unsafe-koodi

C#:ssa voidaan kirjoittaa myös unsafe-koodia, jolloin osoittimia ja natiivimuistia voidaan käsitellä CLR:n automaattisen muistinhallinnan ulkopuolella. Tällainen koodi merkitään unsafe-avainsanalla, ja sitä käytetään tyypillisesti suorituskyvyn optimointiin tai ulkoisten natiivikirjastojen kutsumiseen esimerkiksi P/Invoke-mekanismin avulla.[81]

Esimerkki natiivimuistin allokoinnista ja osoittimen käytöstä unsafe-kontekstissa:

unsafe
{
    int arvo = 10;
    int* p = &arvo;   // p osoittaa muuttujaan arvo-muuttujaan
    *p += 5;          // kasvattaa arvoa osoittimen kautta
}
// arvo on nyt 15
Remove ads

Käyttökohteet ja ekosysteemi

C#:ää käytetään laajasti erilaisissa sovelluskehityksen alueilla osana .NET-ekosysteemiä. Kieltä tuetaan sekä Microsoftin omissa että avoimen lähdekoodin toteutuksissa, ja se soveltuu monialustaiseen kehitykseen Windows-, Linux- ja macOS-ympäristöissä.[2]

Työpöytä- ja mobiilisovellukset

C#:llä kehitetään työpöytä- ja mobiilisovelluksia muun muassa Windows Forms-, WPF- ja .NET MAUI -kehyksillä. .NET MAUI mahdollistaa yhden koodikannan käytön useilla alustoilla, mukaan lukien Windows, macOS, iOS ja Android.[82] Perinteisemmät Windows-työpöytäsovellukset hyödyntävät usein WinUI 3 -käyttöliittymäkirjastoa, kun taas Avalonia-kirjasto mahdollistaa XAML-pohjaisten sovellusten ajamisen useilla alustoilla.

Verkkosovellukset ja pilvipalvelut

Verkkosovelluksia ja -palveluita rakennetaan tyypillisesti ASP.NET Core -alustalla, joka tukee REST-rajapintoja, MVC-arkkitehtuuria sekä minimalistisia Web API -projekteja. Pilvipalveluissa C# on keskeisessä roolissa Microsoft Azure -ympäristössä, jossa sitä käytetään esimerkiksi Azure Functions -serverless-ratkaisuissa, Azure App Service -sovelluksissa ja konttipohjaisissa mikropalveluissa.[83][84]

Pelikehitys

C#-kieltä käytetään pelikehityksessä erityisesti Unity-pelimoottorissa, jota hyödynnetään sekä 2D- että 3D-peleissä useilla alustoilla (PC, konsolit, mobiili, VR). Unityn C#-skriptaus on laajasti käytetty malli kaupallisissa ja indie-peliprojekteissa.[85] Muita C#-tukea tarjoavia pelimoottoreita ovat esimerkiksi Godot ja Stride.

Blazor ja WebAssembly

Blazor mahdollistaa interaktiivisten verkkosovellusten kehittämisen kokonaan C#:llä selaimessa WebAssembly-tekniikan avulla (Blazor WebAssembly) tai palvelinpohjaisesti (Blazor Server). Molemmat mallit jakavat saman koodikannan ja komponenttipohjaisen rakenteen, mikä vähentää tarvetta JavaScript-koodille.[86]

Kehitystyökalut ja kirjastot

C#-kehitystä tuetaan useilla työkaluilla, kuten Microsoft Visual Studiolla, Visual Studio Codella ja JetBrains Riderillä. Virallinen .NET SDK sisältää kääntäjät ja suoritusaikaiset komponentit. Kirjastoja ja komponentteja jaellaan NuGet-paketinhallinnan kautta, joka kattaa laajan valikoiman avoimen lähdekoodin kirjastoja (esimerkiksi Entity Framework Core, Serilog, AutoMapper).[87][88] C#:lle on tarjolla myös koneoppimis- ja data-analytiikkakirjastoja, kuten ML.NET, jota käytetään .NET-pohjaisissa tekoäly- ja data-analytiikkasovelluksissa.[89]

Standardointi ja toteutukset

C#-kielen määrittely ja toteutukset ovat kehittyneet samanaikaisesti Microsoftin johtaman kehityksen ja avoimen standardoinnin myötä. Kielen ydinspecifikaatio on julkaistu avoimesti, ja toteutuksia on useita sekä suljetun että avoimen lähdekoodin lisensseillä.

Ecma- ja ISO-standardit

Microsoft toimitti C#-kielen ja siihen liittyvän CLI-määrityksen standardoitavaksi Ecma Internationalille vuonna 2000. Kielestä tuli Ecma-standardi ECMA-334 joulukuussa 2001 (1. painos), ja standardia on päivitetty useaan otteeseen; uusin 7. painos julkaistiin joulukuussa 2023.[90] Standardi kattaa kielen syntaksin ja semantiikan, mutta ei sido toteutusta tiettyyn kirjastoon tai alustaan.

ECMA-334 hyväksyttiin myös kansainväliseksi standardiksi ISO/IEC 23270:2003 (1. painos) ja myöhemmin ISO/IEC 23270:2018 (3. painos).[91] Standardointi on mahdollistanut vaihtoehtoisten toteutusten kehittämisen ja varmistanut kielen yhteensopivuuden eri alustoilla.

Nykyiset C#-versiot (6.0–14.0) ylittävät virallisen ECMA/ISO-standardin kattavuuden, mutta Microsoft julkaisee täydet kielispesifikaatiot avoimesti GitHubissa osana avoimen kehityksen prosessia.[92]

Microsoft .NET

Virallinen ja laajimmin käytetty toteutus on Microsoftin kehittämä .NET (aiemmin .NET Framework, .NET Core ja Xamarin/Mono). Yhtenäinen .NET-alusta (alkaen .NET 5 vuodesta 2020) sisältää Roslyn-kääntäjän, CLR-suoritusaikaisen ympäristön ja laajan luokkakirjaston. Toteutus tukee Windowsia, Linuxia ja macOS:ää sekä sulautettuja ja mobiilialustoja.[93]

Avoimen lähdekoodin toteutukset (Mono, Roslyn)

Roslyn (.NET Compiler Platform) on Microsoftin vuodesta 2014 avoimeksi tekemä C#- ja Visual Basic -kääntäjäalusta, joka on lisensoitu MIT-lisenssillä ja kehitetään GitHubissa .NET Foundationin alaisuudessa.[94]

Mono-projekti oli pitkään merkittävin täysin avoimen lähdekoodin .NET-toteutus. Vuodesta 2016 se on ollut osa Microsoftin Xamarin/Mono-projektia, ja sen komponentteja on integroitu yhtenäiseen .NET-alustaan. Mono on edelleen käytössä erityisesti Unity-pelimoottorissa.[95]

Versiohistoria

Lisätietoja Versio, Julkaisuvuosi ...

Taulukon tiedot.[96]

Katso myös

Lähteet

Aiheesta muualla

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads