Till en viss grad har man kunnat skapa lokala funktioner i C# under en tid.

public void DoSomething()
{
    Func<int, int> d = (x) => x * 2;
    for (var i = 1; i <= 8; i++)
        Console.WriteLine(d(i));
}

I C# 7 kan man skapa fullvärdiga funktioner i en annan funktion. Det enda som egentligen skiljer en lokal funktion från en medlemsfunktion (eller en static) är att lokala funktioner inte har någon angiven synlighet, eftersom det inte är relevant.

public void DoSomething()
{
    int d(int x) => x * 2;
    for (var i = 1; i <= 8; i++)
        Console.WriteLine(d(i));
}

Fördelen med riktiga lokala funktioner är att det ger tillgång till parameterarrayer, referensparametrar, utparametrar och generics, alltså sådant som inte kunde användas i gamla inline-funktioner.

Closure-hanteringen är lite intressant. Följande exempel använder bara gammal C# och kompilerar inte, eftersom variabeln MinFunktion som håller inlinefunktionen inte finns än.

public void DoSomething()
{
    MinFunktion();
    int x = 10;
    Action MinFunktion = () => x++;
    Console.WriteLine(x);
}

Om vi istället flyttar ner anropet, skrivs 11 ut på skärmen, eftersom x ökas med 1 i MinFunktion.

public void DoSomething()
{
    int x = 10;
    Action MinFunktion = () => x++;
    MinFunktion();
    Console.WriteLine(x);
}

Resultatet blir detsamma om jag ersätter MinFunktion med en fullvärdig lokal funktion. Det x som ökas i MinFunktion är en closure.

public void DoSomething()
{
    int x = 10;
    void MinFunktion() => x++;
    MinFunktion();
    Console.WriteLine(x);
}

Men detta är riktigt intressant. Eftersom MinFunktion nu är en fullvärdig funktion, så skapas den i den yttre funktionen DoSomething vid kompilering. Så nu kan jag anropa den innan variabeln x finns.

public void DoSomething()
{
    MinFunktion();
    int x = 10;
    void MinFunktion() => x++;
    Console.WriteLine(x);
}

Svaret blir då 10, eftersom det inte är closure-variabeln x som blir ökad, utan en x som är lokal för MinFunktion.

Slutligen, eftersom lokala funktioner är fullvärdiga funktioner, kan de innehålla egna lokala funktioner. Snyggt!

Så vad fungerar inte?

Generellt sett kan en värdetyp inte innehålla null, men en referenstyp kan innehålla null. Detta är vettigt, eftersom en referenstyp innehåller just en referens som kan vara eller inte vara satt.

Vi har haft nullable types i C# en tid nu. Det är en struktur som ger värdetyper en null-flagga, så att dessa kan vara null.

public int X { get; set; } //Ej initierad - innehåller 0.
public int? Y { get; set; } //Ej initierad - innehåller null.

Snart ska man kunna förhindra referenstyper från att vara null, vilket är praktiskt t.ex. om man vill att en parameter ska vara obligatorisk. Troligen kommer det att se ut ungefär så här:

public MyType A { get; set; } //Kan innehålla null
public MyType! B { get; set; } = new MyType(); //Kan inte innehålla null

C# 7 ska ha visst stöd för pattern matching, men det fungerar inte (än).

Inte heller record types fungerar. Record types är klasser som endast innehåller properties, som deklareras med en enda rad kod.

Det finns alltså anledning att titta med på C# 7 längre fram.

Category :

Leave a Reply

Your email address will not be published. Required fields are marked *