Funktioner är andra klassens medborgare i C#

I C# 8 är fortfarande funktioner ett slags “andra klassens medborgare” i jämförelse med variabler. Man kan alltså fortfarande göra mer med en variabel än en funktion i C# 8.

Varken variabler eller funktioner kan skapas i namnrymder, båda kan skapas i en klass och båda kan skapas i en funktion, vilket illustreras av detta exempel:

namespace ConsoleApp1
{
    public class MyClass
    {
        public int VariableInClass;

        public void FunctionInClass()
        {
            int variableInFunction;
            
            void functionInFunction() { }
        }
    }
}

Eftersom det finns saker som du kan göra med variabler som du inte kan göra med funktioner, så är funktioner fortfarande att betrakta som ett slags andra klassens medborgare. Du kan inte lagra en funktion i en variabel, du kan inte skicka funktioner som argument och du kan inte använda type inference när du skapar funktioner.

var x = 10;

//Variabel som påverkar x
Action y = () => x++;
y();
Console.WriteLine(x);

//Funktion som påverkar x
void z() => x++;
z();
Console.WriteLine(x);

Att man inte kan lagra funktioner i variabler spelar inte någon jättestor roll. Som namnet antyder beskriver funktioner ofta något funktionellt, vilket i praktiken innebär att de innehåller programsatser. Programsatser kan även lagras i variabler, vilket innebär att funktionalitet kan lagras i en variabel och skickas som argument. Detta exempel visar hur instruktionen att summera två tal lagras i en variabel:

//Add the ability to add
//two integers in x.
Func<int, int, int> x =
    (int a, int b) => a + b;

//Execute the content
//of x, save the result in y.
var y = x(10, 20);

Detta exempel visar hur samma instruktion skickas som parameter till en funktion:

GiveMeFunctionality((a, b) => a + b);

C#-programmets startpunkt kan inte ersättas av en variabel, utan måste vara en riktig funktion kallad Main. I vissa fall kan man kringgå att en funktion inte kan användas som en variabel genom att skapa en variabel som anropar en funktion, och skicka den istället. T.ex. så här:

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            GiveMeFunctionality(
                Functionality);
        }

        static int Functionality
            (int a, int b) =>
            a + b;
        
        static void GiveMeFunctionality(
            Func<int, int, int> x)
        {
            Console.WriteLine(x(10, 20));
        }
    }
}

Men till skillnad från variabler kan funktioner vara rekursiva. En funktion kan alltid anropas från en annan funktion, och tack vara stödet för reflection i C# (som t.ex. C++ saknar) kan funktioner alltid anropas oberoende av var i koden de är deklarerade.

var x = 0;
void i()
{
    x++;
    while (x < 10) 
        i();
}

Motsvarigheten skulle kunna vara något i den här stilen, vilket kräver att variabeln i finns medan den fortfarande deklareras:

var x = 0;
var i = () => {
    x++;
    while (x < 10) 
        i();
};

Leave a Reply

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