Just nu i M3-nätverket
Gå till innehåll

boxin/unboxing


Rickard Liljeberg

Rekommendera Poster

Rickard Liljeberg

can someone shed some light on boxing/unboxing of variables like double for me?

 

in my book it says that you can box a double by doing object a = double b = 5.5;

That is logical i guess although that would mean that object should have a overloaded constructor with a double as parameter (if it were c++ atleast).

 

But the thing is that this works, double a= 5.5; a.ToString(); Thats the part i dont get at all, i mean i havn't boxed it but yet it can apply functions from object.

 

svenska svar går finfint me :-)

 

Länk till kommentar
Dela på andra webbplatser

Det beror på att .NET är typ 100% objekt orienterat. Heltal, flyttal etc ärver även dessa från object.

 

T.ex prova det här:

--- kod -------------

string a = 5.5.ToString();

---------------------

Så även vanliga siffror är faktiskt object och kan konverteras till detta.

 

Detta funkar ju också:

--- kod -------------

object a = 5.5;

---------------------

 

/Mirandir

 

Länk till kommentar
Dela på andra webbplatser

Rickard Liljeberg

Efter att ha forskat lite så har jag insett att int är faktiskt ärvd av object men ändå inte....

 

om jag kör int i = 5;

 

så hamnar 5:an på stacken och inget annat, dvs som en primitiv datatyp, vilket håller ner storleken och snabbar upp.

 

om jag sen gör i.ToString() så skapas ett object på stacken som pekar på en int på heapen och där den pekar på heapen ligger 5:an.

 

så den är lite skum för den är fortfarande en primitiv datatyp men som ändå på nåt vänster luckas ärva object

 

Länk till kommentar
Dela på andra webbplatser

Value types, dvs sådana som ärver från System.ValueType (int, char, struct etc.), är mycket riktigt stack-allokerade (vilket som du säger är bra för prestanda jämfört med att allokera från GC Heapen). När sådana måste bete sig som objekt, allokeras en instans av System.Object från GC heapen som "container" för value-typen. Runtimen vet sedan att objektet innehåller en value type (man har "boxat" en value type i reference typen).

 

Antag att du själv gör detta:

class Test
{
   static void Main()
   {
       double d = 5.5;
       System.Object o = d;
   }
}

 

När du gör "System.Object o = d" görs en implicit boxing av kompilatorn. IL-koden ser ut som:

.method private hidebysig static void  Main() cil managed
{
 .entrypoint
 // Code size       18 (0x12)
 .maxstack  1
 .locals init (float64 V_0,
          object V_1)
 IL_0000:  ldc.r8     5.5
 IL_0009:  stloc.0
 IL_000a:  ldloc.0
 IL_000b:  [b]box[/b]        [mscorlib]System.Double
 IL_0010:  stloc.1
 IL_0011:  ret
} // end of method Test::Main

 

 

När du anropar i.ToString() på din int, är detta inte riktigt samma sak som att "box":a. System.Int32 har ju en overridad ToString(), och det är den som körs och skapar en instans av System.String som returneras.

 

Länk till kommentar
Dela på andra webbplatser

Rickard Liljeberg

Nu sägger mig il koden inte alltför mkt, många kommandon där jag inte förstår (hur får man fram den btw?)

 

men jag förstår poängen, fast jag är inte helt förstående hur object kan peka på en value type.

Är object kapabel till att bli wrapper åt alla klasser eller bara åt vissa?

 

Fast jag trodde jag hittade på ¨msdn att när jag gjorde int i; i.ToString(); så skapade den ett object som wrappade datatypen. för jag tyckte oxå det var märkligt, om nu även datayper är klasser som ärvt av object så borde dom haft en överlagrad ToString() funktion. så jag tyckte det var en anning märkligt.

 

Länk till kommentar
Dela på andra webbplatser

Rickard Liljeberg

Jag kanske skall summera ihop mig lite.

 

Om man gör en manuell konevrtering så förstår jag att det kan fungera utom en sak. hur kan det komma sig att ett object från System.Object kan peka på ett object från en klass som tidigare ärvt från System.Object. Kan fler klasser peka på det viset? och kan System.Object peka på mer än bara datatyper?

 

Om man inte gör det manuellt utan med t.ex int i; i.ToString(); så förstår jag det finfint om int är ärvt av object och därmed har ToString funktionen överlagrad, det är så jag finner mest logiskt men det är inte vad msdn sa om jag tolkade den rätt - är du säker på att så är fallet? (inte för att betvivla dig, men jag vill göra vara helt säker)

 

Länk till kommentar
Dela på andra webbplatser

Jag blir lite förvirrad av de begrepp du använder ("konvertering" och "wrapper" är ord som inte riktigt finns i .Net sammanhang på sättet du använder dem) så jag kanske missförstår hur du menar. Men om jag förstår dig rätt, så tänker du lite fel när det gäller ToString() och blandar ihop detta med "boxing". Vill därför börja med att klargöra detta. Jag vet att du förstår det mesta redan, men en sammanfattning kommer ändå här:

 

 

.Net runtimen (CLR) stöder två slags typer, reference types och value types. Reference types är *alltid* allokerade från den garbage collectade heapen. Detta har som du säger en negativ på verkan på prestanda. Därför tillhandahåller CLR även lättviktsvarianten value types för vanliga typer som bland annat integers och bytes. Value types allokeras på den exekeverande trådens stack (dvs ingen GC inblandad vilket är mycket snabbare).

 

I den bistra verkligheten måste man dock ibland ha även dessa enkla typer allokerade på heapen (för att kunna lagra i listor etc.) och då kan man representera en value type inuti en reference type. När en value type är representerad inuti en reference type på det sättet heter det att value typen är i boxed form (annars är den unboxed). Reference types är däremot alltid representerade som "boxed".

 

Att ändra value typens representation från unboxed till boxed är förstås vad som kallas "boxing". Hur boxing fungerar beror till en början av vilket språk man använder, jag använder C# syntax nedan. C# boxar automatiskt åt dig och det är bra att förstå när detta händer (vilket jag antar är vad du håller på att lära dig :-) ). I exempelvis managed c++ måste man manuellt boxa om det behövs.

 

Enkel form av boxing är:

int i = 5; // i är "unboxed"
Object o = i; // i är nu "boxed" inuti o

För att konstatera att o faktiskt är en value type:

Console.WriteLine(i is ValueType);
Console.WriteLine(o is ValueType);

Båda dessa kommer skriva ut "True".

 

 

Nu till något jag tror du förväxlar med boxing, nämligen ToString(). ToString() innebär alltså inte att du boxar eller "wrappar" något!

 

Att använda ToString() på något, exempelvis "int i = 5; string s = i.ToString();" innebär att ett helt nytt sträng-objekt (System.String) allokeras från GC-heapen. Man kan konstatera att:

 

1. Ingen boxing är inblandad. Detta kanske behöver en ytterligare förklaring. "i" är ju en "unboxed" (kan man säga oboxad??) value type. Då är C# kompilatorn smart och ser att int (som ju egentligen är System.Int32) override:ar ToString() och lägger ut kod för att anropa denna (och behöver således aldrig boxa "i").

 

2. Denna sträng har ingen som helst koppling tillbaka till "i". (Ditt påstående "[kursiv]om jag sen gör i.ToString() så skapas ett object på stacken som pekar på en int på heapen och där den pekar på heapen ligger 5:an.[/kursiv]" är alltså felaktigt)

 

 

 

Kommentarer kring några dina frågor:

 

 

> hur får man fram den btw?

 

Öppna din kompilerade .exe-fil med ildasm.exe, som finns under %ProgramFiles%\Microsoft.NET\SDK\v1.1\Bin.

 

 

 

> ...hur object kan peka på en value type. Är

> object kapabel till att bli wrapper åt alla

> klasser eller bara åt vissa?

 

Antag att vi har deklarerat "Object o;". Vi har då fallen att vi gör tilldelar någon value type eller någon reference type till "o":

 

* Value type (exempelvis o = 3;). Det som händer är att man boxar value typen Int32, med värdet 3, in i o. Vi har ju redan konstaterat att boxingen innebär att man allokerar lite plats från GC heapen för den plats som value typen tar (här är det ju 32 bits, dvs 4 bytes) samt lite overhead som objektet tar och som runtimen behöver.

 

* Reference type (exempelvis "Object o; System.Random r = new System.Random(); o = r;". Här görs egentligen ingenting. Det är precis som i C++ där man kan ha en pekare av basklass-typ till ett visst objekt. Object är alltså ingen "wrapper" utan bara en generalisering till något högre i arvshierarkin.

 

Object "pekar" således aldrig på en value type, utan object kan vara en "value type i boxad form".

 

 

 

> ...Om man inte gör det manuellt utan med t.ex int i;

> i.ToString(); så förstår jag det finfint om int är

> ärvt av object och därmed har ToString funktionen

> överlagrad,

 

Det är alltså här C# kompilatorn är smart och förstår att den ska anropa instansmetoden Int32.ToString() för i.

 

Länk till kommentar
Dela på andra webbplatser

Jag kan tipsa om en bra MSDN-artikel av Jeffrey Richter som tar upp detta, nämligen: http://msdn.microsoft.com/msdnmag/issues/1200/dotnet/dotnet1200.asp

 

Varje utvecklare som använder .Net borde förresten ha ett tummat exemplar av Richters bok "Applied Microsoft .NET Framework Programming": http://wintellect.com/resources/amazoninfo.aspx?asin=0735614229

 

Länk till kommentar
Dela på andra webbplatser

Rickard Liljeberg

Stort tack för den långa och ingående förklaringen, det är precis vad jag behövde!

 

Jag har Jeffrey's bok liggandes här bredvid mig, fortfarande inplastad. det är nämligen så att jag skall bli lärare i asp.NET så högskolan köpte in dom böckerna jag ösnkade där det var en av dom. Nu har jag inte hunnit så långt som den än utan håller på med http://booksonline.idg.se//prod_sida.asp?isbn=0735619352 vilken jag inte tycker är att rekomendera alls.

 

Har du fler böcker som du rekommenderar så är jag idel öra.

 

Återigen, tack för hjälpen!

 

Länk till kommentar
Dela på andra webbplatser

Angående böcker så finns det en speciell avdelning på www.asp.net forum som handlar om böcker och boktips... :)

 

 

_________

-- ante --

 

 

Länk till kommentar
Dela på andra webbplatser

Magnus Gladh

Riktigt bra inlägg.

 

En fråga då, nu när du verkar så påläst. Jag har en klass som har en variabler av typen System.Data.SqlTypes.

 

Om jag nu vill göra om en Int32 till en System.Data.SqlTypes.SqlInt32 så kan jag gå 2 vägar.

 

SqlInt32 test = (SqlInt32) myInt32Value;
SqlInt32 test = SqlInt32.Parse(myInt32Value.ToString());

 

Boxar/unboxar båda dessa operationer eller är det så att den undre låter bli att boxa/unboxa och på detta vis bli lite snabbare?

 

- Magnus

-----------------------------------------------------

Ropen skalla, BBB (eller BOSTREAM) åt alla!!!

 

Länk till kommentar
Dela på andra webbplatser

SqlInt32 är (precis som Int32) en value type, alltså behöver inget boxas.

 

I första fallet anropas bara SqlInt32-konstruktorn med myInt32Value som argument och en SqlInt32 läggs på stacken.

 

I det andra fallet anropas Int32.ToString() som allokerar en sträng på GC heapen, vilket är onödigt. Dessutom ska den parsas av SqlInt32.Parse, vilket också är onödigt.

 

Det första alternativet är klart att föredra.

 

Länk till kommentar
Dela på andra webbplatser

Rickard Liljeberg

Nästan otroligt, det visste tom jag :)

 

en fråga om skillnad mellan c# och c++ dock.

 

i c++ är det väl så här

 

int kalle = 5; //allokeras på stacken

int* kalle = new int(5); //allokeras på heapen

 

men så verkar det inte vara i c# utan här kan man skriva både

 

int kalle = 5;

och

int kalle = new int(5);

 

och i båda fallen så avgör kompilatorn om den skall hamna på stacken eller heapen pga att den är intelligent. har jag rätt så långt?

 

då kan jag egentligen passa på att fråga en kvick fråga som jag annars hade slagit upp, men nu när jag ändå skriver så...

 

i c# så finns a) inga pekare eller B) allt är pekare eller c) ngt annat ;-)

 

Länk till kommentar
Dela på andra webbplatser

c++ är det väl så här

 

int kalle = 5; //allokeras på stacken

int* kalle = new int(5); //allokeras på heapen

Sant.

 

...i c#...

...avgör kompilatorn om den skall hamna på stacken eller heapen...

Fel, det beror på typen. Klasser hamnar alltid på heapen. Value types (sådant som ärver av System.ValueType, exempelvis System.Int32, System.Enum och struct:ar) hamnar normalt på stacken (men kan även hamna, som members i en klass, hamna inbäddade på heapen).

 

Begreppet pekare används inte i C#. Däremot används pekare implicit. När du skickar en reference type som parameter till en funktion är det bara en pekare som skickas dit, inte hela objektet (skulle innebära onödigt mycket kopiering).

 

Pekare för att direkt komma åt en viss adress i minnet finns normalt sett inte. Anledningen är att man inte skulle kunna garantera säkerhet och stabilitet, eftersom man då skulle kunna gå förbi typsäkerhet, permission-kontroller eller på något annat sätt förstöra datastrukturer som runtimen använder. Dock kan man kompilera C# program med flaggan "/unsafe" och ändå kunna hämta ut minnespekare och jobba direkt med dessa. Normalt bör man förstås inte göra detta.

 

Exempelvis en administratör av en webfarm, där godtyckliga programmerares applikationer får köras, stänger typiskt av möjligheten att köra unsafe code. (CLR kan göra sådana typer av kontroller innan den tillåter assemblies att laddas)

 

Länk till kommentar
Dela på andra webbplatser

Magnus Gladh

Okej tack för det.

 

Så alla de typer som är typer i SqlType och DBType därs motsvarande är en valueTyp är också en valueType.

 

Så SqlString är en referenstyp medans SqlInt64 är en value.

 

Då blev det lite klarare.

 

- Magnus

-----------------------------------------------------

Ropen skalla, BBB (eller BOSTREAM) åt alla!!!

 

Länk till kommentar
Dela på andra webbplatser

Dagens meningslösa inlägg av undertecknad, men jag har retat mig på titeln rätt länge nu :)

 

--

.Wey

 

Future Hero Next Generati0n

 

Länk till kommentar
Dela på andra webbplatser

Rickard Liljeberg

fast det borde vara boxing dvs en handling inte box-in som är nån form av order som säger att en box skall in nånstanns......

 

men visst jag har själv sett det saknade g:et ....

 

Länk till kommentar
Dela på andra webbplatser

fast det borde vara boxing dvs en handling inte box-in som är nån form av order som säger att en box skall in nånstanns......

 

Jag associerade mer till wax-on, wax-off - men i alla fall :)

 

--

.Wey

 

Future Hero Next Generati0n

 

Länk till kommentar
Dela på andra webbplatser

> Så SqlString är en referenstyp medans

> SqlInt64 är en value.

 

Njae, om man ser efter så är faktiskt även SqlString en value type. Sedan har den en System.String medlemsvariabel som ju förstås är en reference type. Poängen är nog att man om strängen är null ska slippa allokera något på heapen.

 

Länk till kommentar
Dela på andra webbplatser

Arkiverat

Det här ämnet är nu arkiverat och är stängt för ytterligare svar.

×
×
  • Skapa nytt...