C# esetén kezelési szempontból kétféle tÃpus létezik. Vannak az értéktÃpusok és a referenciatÃpusok. Az értéktÃpusok minden függvényhÃvás esetén értékként lesznek átadva. Ez azt jelenti, hogy konkrétan nem a változó van átadva a metódusnak hÃváskor, hanem annak az értéke, vagyis hÃvás elÅ‘tt másolódik az érték, tehát az adat nagyon rövid ideig 2x van benne a memóriában.
Ennek a viselkedésmódnak egyszerű tÃpusok, mint számok, szövegek1 esetén van értelme. De nem csak ezen tÃpusok viselkednek Ãgy, hanem minden struktúrából származtatott objektum. Az egyszerű tÃpusok egyébként struktúraként vannak megvalósÃtva a keretrendszerben.
A referenciatÃpusok lényegében az osztályok. Ezek esetén a metódushÃváskor az osztály referenciája másolódik, ami azt jelenti, hogy csak egyszer van jelen az objektum a memóriában. Ez nagy mennyiségű adat tárolásánál jó, de néha problémát okoz, ha referencia helyett értékre lenne szükségünk.
A nagy mennyiségű adattárolás igen relatÃv fogalom lehet. LehetÅ‘ség szerint ha a megvalósÃtandó tÃpusunknak több, mint 16 byte adatot kell tárolnia, akkor class kulcsszóval lássuk el, vagyis legyen osztály. Ha a tárolt adatok mennyisége ennél kevesebb, akkor lehet nyugodtan struktúra.
Az alábbi példa egy egyszerű struktúra létrehozását mutatja be:
using System;
namespace PeldaStruktura
{
public struct Pont
{
//16 byte adat :)
public double x, y;
}
class Program
{
static void Main(string[] args)
{
//nem kell hÃvni konstruktort
Pont pont;
//de cserébe minden tagot inicializálni kell értékkel
//ha ez elmarad, akkor fordÃtási hibát kapunk
pont.x = 23;
pont.y = 11;
Console.WriteLine("x: {0}, y: {1}", pont.x, pont.y);
Console.ReadKey();
}
}
}
A program kimenete:
x: 23, y: 11
A következő példa pedig az értékátadás problémáit mutatja be:
using System;
namespace PeldaAtadas
{
class Osztaly
{
public double ertek;
}
class Program
{
static void ErtekatadoPelda(double ertek)
{
ertek = 2.1;
}
static void ReferenciaPelda(Osztaly referencia)
{
referencia.ertek = 2.1;
}
static void Main(string[] args)
{
Console.WriteLine("Érték tÃpus példa");
Console.WriteLine();
double ertek = 3.14;
Console.WriteLine("FüggvényhÃvás elÅ‘tt az ertek: {0}", ertek);
ErtekatadoPelda(ertek);
Console.WriteLine("FüggvényhÃvás után az ertek: {0}", ertek);
Console.WriteLine();
Console.WriteLine("Referencia tÃpus példa");
Console.WriteLine();
Osztaly o = new Osztaly();
o.ertek = 3.14;
Console.WriteLine("FüggvényhÃvás elÅ‘tt az ertek: {0}", o.ertek);
ReferenciaPelda(o);
Console.WriteLine("FüggvényhÃvás után az ertek: {0}", o.ertek);
Console.ReadKey();
}
}
}
A program kimenete:
Érték típus példa
Függvényhívás elÅ‘tt az ertek: 3,14
Függvényhívás után az ertek: 3,14
Referencia típus példa
Függvényhívás elÅ‘tt az ertek: 3,14
Függvényhívás után az ertek: 2,1
A példa esetén jól látható, hogy az érték tÃpusok esetén, ha egy függvény megpróbálja a függvényen belül módosÃtani a paraméterként kapott értéket, akkor csak a függvényen belül lesz maradandó a változás, mivel az eredeti érték másolatát kapta meg. A függvényhÃvás végeztével az eredetileg átadott érték nem módosul, mÃg a referencia tÃpusoknál igen, mert ott ténylegesen a változó kerül átadásra, nem annak értéke.
Nézzünk még egy példát:
using System;
namespace PeldaAtadasKomplikalt
{
public class Osztaly
{
public double Ertek { get; }
public Osztaly(double ertek)
{
Ertek = ertek;
}
public void Kiir()
{
Console.WriteLine(Ertek);
}
}
class Program
{
public static void Modosit(Osztaly osztaly)
{
osztaly = new Osztaly(99);
}
static void Main(string[] args)
{
var pelda = new Osztaly(44);
pelda.Kiir();
Modosit(pelda);
pelda.Kiir();
Console.ReadKey();
}
}
}
A program kimenete:
44
44
Az eredmény meglepÅ‘ lehet, Ãgy elemezzük mi is történik itt. A Modosit metódus egy Osztaly tÃpusú referencia tÃpust vár. Meg is kapja a pelda referencia másolatát, ami felülÃr egy új Osztaly tÃpusú referenciával. Mivel a másolatot kapta meg, ezért a metódusból kilépve ez a másolt referencia megszűnik létezni a módosÃtott értékével együtt. Ezért marad a pelda ugyan az mind a két kiÃrás között.
Összegezve: a metódus meg tudja változtatni a paraméterül kapott referencia tÃpusú objektumot, viszont a referenciát nem tudja egy másik objektumhoz rendelni, erre lesz majd jó a ref módosÃtó.
-
A .NET esetén a string tÃpus valójában egy osztály, ami igyekszik úgy viselkedni, mint egy érték tÃpus. Ez nem tervezési hiba, a keretrendszer sebességét növeli.↩