Mobiele apps programmeren met Xamarin – De anatomie van een C#-programma – Eigen functies aanmaken

print

Inhoud


Wat vooraf ging

We werken opnieuw met Xamarin.

  • Start een nieuw Xamarin-project op en geef het een passende naam (ik kies voor de inspirerende naam App3).
  • Eens het programma opgestart is gaat u naar het bestand MainPage.xaml.
  • Merk het Label op. We gaan dit label gebruiken als uitvoer. Het label moet dus aanspreekbaar zijn vanuit de achterliggende programmeercode en dient dus een naam te krijgen. Volgende code kent de naam lblUitvoer toe aan het label.

x:Name="lblUitvoer"

  • Merk de x: voor de Name op! Die staat er omdat het attribuut Name uit de namespace “xmlns:x=”http://schemas.microsoft.com/winfx/2009/xaml” komt die een x als prefix heeft.
  • De aangepaste XAML ziet er als volgt uit.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:App3" x:Class="App3.MainPage">

	<Label x:Name="lblUitvoer" Text="Welcome to Xamarin Forms!" VerticalOptions="Center" HorizontalOptions="Center" />

</ContentPage>
  • Vervolgens klikt u op MainPage.xaml.cs, dit is de code-behind.

Functies

Herinnert u zich nog het DRY-principe?

Het DRY-principe stelt dat u eenzelfde code nooit twee keer mag intypen.

Dus, wanneer bepaalde code, bv. de code om iets te berekenen, herhaaldelijk kan voorkomen in uw programma(‘s) dan kunt u dit het best afzonderen in een functie (methode, subroutine, module, procedure,…).

Functies zorgen er dus voor dat u bepaalde code slechts eenmaal moet programmeren. Eventuele wijzigingen moeten dan ook maar op deze ene plek uitgevoerd worden. Hebt u deze functie nodig dan moet u deze aanroepen.

  1. Een functie moet gedefinieerd worden, door het een naam/identifier te geven.
  2. Nadien kunt u deze functie aanroepen door deze naam te gebruiken en de eventuele parameters mee te geven.
  3. Uw functies kunt u in principe gelijk waar definiëren. Heel vaak maken functies echter deel uit van een specifieke klasse. We spreken dan niet meer van functies maar van methoden.

Een functie definiëren

Het definiëren van een functie/methode in C# gebeurt als volgt:

visibility return type name(parameters)
{
statement(s)
}

  • Een functie heeft dus een zeker visibility (een bereik of een “Accessibility Level”). De twee meest gebruikte zijn public (er is geen beperking tot de toegankelijkheid) en private (enkel toegankelijk binnen de klasse waar de functie zelf toe behoort. Weet dat ook bv. variabelen kunnen beperkt worden in hun toegankelijkheid door toevoeging van een visibility.
  • Vervolgens komt het return type. Dit is het datatype van het resultaat dat de functie teruggeeft. Geeft de functie niets terug dan is het return type van het type void.
  • Nadien volgt de functienaam, die een identifiër is en dus moet voldoen aan de regels van een identifiër.
  • nadien komen de ().
  • Een functie kan parameters/argumenten ontvangen. Deze parameters zijn waarden waarmee de functie iets kan/moet doen, parameters zijn de invoer (input) voor de functie. Parameters maken functies dynamischer doordat u steeds andere waarden kunt toekennen aan de parameters waardoor de functie een ander resultaat kan “berekenen”.
  • Zijn er meerdere parameters dan worden deze gescheiden door een komma.
  • Het begin en einde van de functie wordt afgebakend door de {}.
  • De functie zelf bevat een (aantal) statement(s).

Er zijn 2 soorten functies.

  • Functies die geen waarde teruggeven.
  • Functies die een “berekende” waarde teruggeven.

Functies die geen waarde teruggeven

Functies die geen waarde teruggeven groeperen een aantal statements onder een specifiek naam om deze gezamenlijk uit te voeren.

Deze functies kunnen, maar moeten geen, parameters hebben. Deze functies kunnen gewoon worden aangeroepen als een statement.

Een reeds gekend voorbeeld is:

InitializeComponent();

Voorbeeld

Een voorbeeld van een publieke functie zonder een terug te geven resultaat (de functie voert gewoon de statements uit in de exacte volgorde maar doet geen specifieke berekening/bewerking die teruggegeven wordt) is:

public void openWebsite(string Website)
{
     Device.OpenUri(new System.Uri(Website));
}

Om deze functie aan te roepen gebruikt de naam openWebsite en geeft u tussen () de gewenste parameters mee, als die er al zijn. Hier wordt de URL van de website die moet getoond worden meegegeven.

openWebsite("https://ictopleidingen.azurewebsites.net/");

U plaatst deze functie binnen de klasse waar ze toe behoort (bv. binnen de pagina waarin ze wordt gebruikt). De volledige programmeercode in Xamarin:

using System;
using Xamarin.Forms;

namespace App3
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            openWebsite("https://ictopleidingen.azurewebsites.net/");
        }

        public void openWebsite(string Website)
        {
            Device.OpenUri(new System.Uri(Website));
        }
    }
}

De reden om deze functie aan te maken kan zijn omdat u de functie openWebsite() gewoon eenvoudiger vindt om te hergebruiken. Maar eigenlijk is dit gewoon een voorbeeldje.


Functies die een waarde teruggeven

Deze functies kunnen (zullen vaak) parameters hebben die de waarden meegeven waarmee de “berekening” moet worden uitgevoerd. Mits deze functies een “berekende” waarde teruggeven moet deze teruggeven waarde ook ergens in bewaard worden. Deze functies worden dan ook meestal aangeroepen rechts van een =-teken (Links staat dan bv. een variabele waar de teruggegeven, berekende, waarde in bewaard wordt) of als parameter van een statement. Ik plaats “berekenen” tussen “” omdat het niet echt om wiskundige berekening moet gaan.

Een reeds gekend voorbeeld is:

Willekeurig.Next()

Hierbij is Willekeurig een variabele was van het type Random().

Deze functie, die een waarde teruggeeft, wordt gebruikt aan de rechterkant van een toekenning (=-teken). In een vorig voorbeeld werd Willekeurig.Next() als volgt gebruikt:

Resultaat += "Willekeurige gehele waarde: " + Willekeurig.Next() + Environment.NewLine;

Een voorbeeld van een publieke functie die een integer teruggeeft is. De naam van de functie is Optellen, er worden 2 parameters van het type integer meegegeven en de waarde die teruggegeven wordt is eveneens van het type integer.


public int Optellen(int getal1, int getal2)
{
int resultaat = getal1 + getal2;
return resultaat;
}

De aanroep van deze functie kan bv. door onderstaande code.

int Uitkomst = Optellen(5,2);

Voorbeeld

Een functie die de BTW berekent kan een praktische functie zijn omdat u deze misschien/wellicht vakker zult nodig hebben.

De functie krijgt de naam BTW is public en geeft een waarde van het type decimal terug. De parameters zijn beiden van het type integer (dit zou bv. ook van een ander type kunnen zijn).

public decimal BTW(int bedrag, int perc)
{
    decimal resultaat = bedrag * perc / 100;
    return resultaat;
}

Deze functie kan bijvoorbeeld aangeroepen via onderstaande code:

int bedrag = 100;
int percentage = 21;
decimal btwBedrag = 0;

btwBedrag = BTW(bedrag, percentage);

lblUitvoer.Text = "De btw bedraagt € " + btwBedrag.ToString("F2") + ".";

Bij de aanroep van de functie BTW wordt de waarde van de variabele bedrag toegekend aan de parameter bedrag van de functie en wordt de waarde van de parameter percentage toegekend aan de parameter perc. Belangrijk hierbij is dat de datatypes overeenkomen (de namen hoeven, zoals u ziet, niet overeen te komen). Uiteraard wordt de eerste parameter bij de aanroep toegekende aan de eerste parameter in de functie, de tweede aan de tweede enz.

BTW(bedrag, percentage)

De functie gebruikt de parameters bedrag en perc (en de hun toegekende waarden) om de gewenste berekening te maken en kent het resultaat toe aan een variabele resultaat.

decimal resultaat = bedrag * perc / 100;

De variabele resultaat wordt nadien teruggegeven.

return resultaat;

De teruggegeven waarde wordt geplaatst in de variabele BTWbedrag. Deze variabele moet van hetzelfde datatype zijn als het datatype van de teruggegeven waarde of, u moet conversie toepassen op de ontvangen waarde.

BTWbedrag = BTW(bedrag, percentage);

De waarde van BTWbedrag wordt vervolgens getoond. De uitvoer wordt geformatteerd met 2 cijfers na de komma btwBedrag.ToString("F2").

lblUitvoer.Text = "De btw bedraagt € " + btwBedrag.ToString("F2") + ".";

De volledige programmeercode in Xamarin.

using Xamarin.Forms;

namespace App3
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            int bedrag = 100;
            int percentage = 21;
            decimal btwBedrag = 0;

            btwBedrag = BTW(bedrag, percentage);

            lblUitvoer.Text = "De btw bedraagt € " + btwBedrag.ToString("F2") + ".";
        }

        public decimal BTW(int bedrag, int perc)
        {
            decimal resultaat = bedrag * perc / 100;
            return resultaat;
        }

    }
}
  • Merk op dat de namen van de parameters van de functie BTW(Bedrag, Perc) en van de aanroep BTW(bedrag, percentage) dezelfde mogen, maar niet moeten zijn.
  • Uiteraard wordt de waarde van de eerste parameter van de aanroep aan de eerste parameter van de functie toegekend. De waarde van de tweede parameter van de aanroep wordt aan de tweede parameter van de functie toegekend, enz.
  • Bij een strongly typed programmeertaal zullen de datatypes van de meegegeven waarde bij de aanroep en van de ontvangen parameter in de functie moeten bepaald en van hetzelfde datatype zijn.

IntelliSense

Gebruik IntelliSense om te achterhalen hoe de functie/methode moet gebruikt worden.

Optionele parameters

Indien u een parameter reeds een standaardwaarde meegeeft,

public decimal BTW(int bedrag, int perc = 21)

mag u deze parameter in de aanroep weglaten. De reeds toegewezen standaardwaarde (hier 21) wordt dan gebruikt.

BTW(bedrag)

Ook hier komt IntelliSense te hulp. Optionele parameters en hun standaardwaarde worden tussen [] weergegeven?


Static

Een static functie kan aangeroepen zonder dat er een instantie van de klasse genomen moet worden. De functie wordt aangeroepen door de naam van de klasse gevolgd door een punt en vervolgens de eigenlijke functie/methode. Een static functie kan ook maar enkel werken met de meegegeven parameters. Een praktisch voorbeeld Math Class die we reeds besproken hebben. Een static klasse kan enkel maar “members” bevatten die ook static zijn.

Oké, bovenstaande zal u zonder een degelijke kennis van klassen niet veel wijzer maken en op dit ogenblik is het nog niet het geschikte moment om hier veel dieper op in te gaan. Maar toch haal ik het al even aan omdat u ermee geconfronteerd kunt worden.

Bekijk eens onderstaande eenvoudige code.

Op zich niets mis mee en deze functie zou dan ook perfect kunnen werken in Xamarin (los van Console.WriteLine(Boodschaptekst); die verwijst naar de console, Xamarin kent Debug.WriteLine(Boodschaptekst); als alternatief) omdat de hoofdfunctie van Xamarin niet static is.

public MainPage()

Maar we werken hier niet in Xamarin maar in een console-omgeving en de hoofdfunctie is static.

public static void Main()

Deze console-applicatie levert volgende foutmelding op “An object reference is required for the non-static field, method, or property ‘Program.Boodschap(string)’“. Waarom dit zo is leest u uitgebreid hier maar de hoofdreden is dat een static functie enkel rechtstreeks naar andere static-functies kan verwijzen of dat u anders een instantie van de klasse (hier class Program) moet nemen.

Ok, misschien ben u nog niet veel wijzer, laten we dan zien hoe we het probleem oplossen.

Oplossing 1, maak uw eigen functie eveneens static door toevoeging van het woordje static.

public static void Boodschap(string Boodschaptekst)

Oplossing 2, neem een instantie van de klasse (hier class Program) en gebruik dit object om de functie/methode aan te roepen.

Program P = new Program();
P.toonBoodschap("Hello, world!");

Gebruik één van de twee oplossingen en gebruik ze niet samen of u krijgt een andere foutmelding.

Probeer dit eens uit in bovenstaande console.


Referentie

Bekijk onderstaande code.

using System;
					
public class Program
{
	public static void Main()
	{
		int getal = 5;

		Optellen(getal); 
		
		Console.WriteLine(getal);
	}
	
	public static void Optellen(int getal){
	
		getal = getal + 1;
		
	}
}

Misschien had u verwacht dat de uitkomst 6 zou zijn, tenslotte typt u bij 5 toch 1 op, of niet?

Ja en nee.

Ja, u geeft de variabele getal met de waarde 5 mee aan de functie en binnen deze functie wordt er 1 bijgeteld.

Nee, want u geeft niet de variabele getal zelf mee maar een kopie van de waarde van deze variabele en het is bij deze kopie dat er 1 wordt bijgeteld. De originele variabele getal, die ook getoond wordt, is dus niet gewijzigd!

Wilt u dat de variabele die u meegeeft ook effectief zelf gewijzigd wordt dan moet u deze variabele meegeven als een referentie. U doet dit door zowel bij de aanroep als bij de betreffende parameter het woordje ref toe te voegen.

Bij de aanroep:

Optellen(ref getal);

Bij de parameter:

public static void Optellen(ref int getal)

De code wordt nu:

using System;
					
public class Program
{
	public static void Main()
	{
		int getal = 5;

		Optellen(ref getal); 
		
		Console.WriteLine(getal);
	}
	
	public static void Optellen(ref int getal){
	
		getal = getal + 1;
		
	}
}

U kunt dit hieronder uittesten.


Recursie

Recursie is het zichzelf opnieuw aanroepen, een recursieve functie is dus een functie die zichzelf, binnen de functie opnieuw aanroept.

Het klassieke voorbeeld is faculteit berekening.

Misschien eerst even kort herhalen wat faculteit precies is op basis van onderstaand voorbeeld:

5! = 5 *4 * 3 * 2 * 1
4! = 4 * 3 * 2 * 1
3! = 3 * 2 * 1
2! = 2 * 1
1! = 1
0! = 1
Ofwel
5! = 5 * 4!
4! = 4 * 3!
3! = 3 * 2!
2! = 2 * 1!
1! = 1 * 0!

Onderstaand voorbeeld toont een recursieve functie voor de berekening van de faculteit.

using System;
					
public class Program
{
	public static void Main()
	{
		Console.WriteLine("Faculteit van 5: " + Faculteit(5));
	}
	
	public static Int32 Faculteit(Int32 Facwaarde)
	{
		if (Facwaarde == 1)
		{
			return 1;
		}
		else
		{
			return Facwaarde * Faculteit(Facwaarde - 1);
		}
	}
}

Probeer het zelf eens.

Er wordt hier reeds gebruik gemaakt van de if(). Deze if() komt in een volgende post uitgebreid aan bod.


Overload

Overloading is een techniek waardoor u meerdere functies dezelfde naam kunt toekennen als ze maar een parameter hebben van een ander datatype (of als de parameter structuur anders is.

Stel u wilt een functie schrijven die een dat toont. Deze functie kan er simpel als volgt uitzien.

public static void toonDatum(DateTime Datum){
	CultureInfo nlBEFormaat = new CultureInfo("nl-BE");
		
	Console.WriteLine(Datum.ToString("dd dddd MMMM yyyy", nlBEFormaat));
}

Merk op dat ik ook nog eerst de juiste “culture” geduid heb voor de correcte weergave.

Deze functie verwacht een parameter van het type DateTime, maar wat als u de datum gewoon als een string wilt ingeven. Dit kan (uiteraard) en u kunt dit zelfs doen door gebruik te maken van overloading. U behoudt dus dezelfde functienaam maar gebruikt een ander datatype als parameter.

public static void toonDatum(string Datum){
	CultureInfo nlBEFormaat = new CultureInfo("nl-BE");

	DateTime dt = Convert.ToDateTime(Datum);
		
	Console.WriteLine(Datum.ToString("dd dddd MMMM yyyy", nlBEFormaat));
}

Deze functie verwacht nu een string als parameter en bevat ook nog een bijkomende conversie:

DateTime dt = Convert.ToDateTime(Datum);

Het aanroepen van deze functie kan nu op twee manieren, met een string en met een DateTime.

toonDatum(DateTime.Now);
toonDatum("08/31/2017");

Merk wel op dat ik hier de datum in Amerikaanse notatie heb meegegeven (dit omdat de test via internet standaard het Amerikaanse formaat hanteert).


Try… catch…

Herneem het bovenstaande voorbeeld eens en typ eens een niet correcte datum in.

De conversie zal niet lukken en u krijgt een foutmelding: The string was not recognized as a valid DateTime. There is an unknown word starting at index 0.. Op zich zou het uiteraard handiger zijn dat uw programma niet “blokkeert” en dat u een aangepaste foutmelding krijgt. Dit kunt u eenvoudig bekomen met volgende twee stappen: try{…} catch{…}.

De code die uitgevoerd wordt, en die kan een fout genereren, plaatst u tussen een try-block:

try
{
	CultureInfo nlBEFormaat = new CultureInfo("nl-BE");			
	DateTime dt = Convert.ToDateTime(Datum);			
	Console.WriteLine(dt.ToString("dd dddd MMMM yyyy", nlBEFormaat));
}

Vervolgens komt er een catch-block waarin de code staat die moet uitgevoerd worden als de try een fout genereert, bv. een passende foutmelding.

catch
{
	Console.WriteLine("U gaf geen of een foutieve datum in!");
}

U kunt hier de code zelf uittesten.

We gaan deze try{…} catch{…} nog wel vaker tegenkomen maar wie nu al meer wilt weten kan terecht in de officiële documentatie.


Expression-bodied

Expression-bodied is een vrij recente toevoeging aan C# en kan gebruikt worden om de programmeercode leesbaarder te maken. Expression-bodied kunnen gebruikt worden als vervanging van een functie/methode die maar uit 1 lijn programmeercode bestaat.

De algemene syntax is:

member => expression;

We hernemen even onderstaande functie die een website opende:

using System;
using Xamarin.Forms;

namespace App3
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            openWebsite("https://ictopleidingen.azurewebsites.net/");
        }

        public void openWebsite(string Website)
        {
            Device.OpenUri(new System.Uri(Website));
        }
    }
}

De bovenstaande code herschrijven als Expression-bodied zou er als volgt uitzien:

using System;
using Xamarin.Forms;

namespace App3
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            openWebsite("https://ictopleidingen.azurewebsites.net/");
        }

        public void openWebsite(string website) => Device.OpenUri(new System.Uri(website));
    }
}

Behandelde Basiscompetenties uit de module ICT Programmeren – Specifieke ontwikkelomgeving: eenvoudige functionaliteiten

  • IC BC017 – kan ICT veilig en duurzaam gebruiken
  • IC BC234 – kan de basisprincipes van programmeren in een specifieke ontwikkelomgeving toepassen
  • IC BC236 – kan eenvoudige wijzigingen aan een programma aanbrengen
  • IC BC241 – kan een programma in een specifieke ontwikkelomgeving maken
  • IC BC248 – kan bouwstenen voor een specifieke ontwikkelomgeving maken

Geef een reactie

  • Abonneer je op deze website d.m.v. e-mail

    Voer je e-mailadres in om je in te schrijven op deze website en e-mailmeldingen te ontvangen van nieuwe berichten.