Dlaczego int jest strukturą?

0

Kiedy zacząłem się uczyć C#, nie rozumiałem, dlaczego kompilator zamienia int na System.Int32. I dalej nie rozumiem. Na SO piszą:

The biggest difference between Java's and C#'s handling of primitives is that you can use C# primitives in places where user-defined struct types can go, most notably, in C# generic arguments, while Java treats primitives as a completely separate group of types.

Czyli chodzi o generyki? Nie da się zrobić, aby generyki obsługiwały jednocześnie obiekty i typy prymitywne takie jak w Javie? No i o co chodzi z tym, że There is no "wrapping" in a struct going on here. i But that happens only if you cast the int to object.?

0

int I System.Int32 w csharp to to samo, czyli autoboxing jest automatyczny, mozna uzywac prymitywow, jako parametrow(w generykach). Inaczej niz, w javie.

0

Tylko z tego, co rozumiem, to .NET jakoś inaczej traktuje System.Int32 niż zwykłe struktury, aby nie było narzutu z opakowywaniem. Tylko po co tak komplikować?

2

Na jakiej podstawie tak twierdzisz? O ile mi wiadomo wszystkie typy wartościowe są obsługiwane tak samo. I boxing następuje tylko w określonych przypadkach, jak przy przypisaniu typu wartościowego do objecta albo do interfejsu, ale już nie przy użyciu jako generyczny typ.

0

A po to żeby mieć jeden standard dla całej platformy .NET, a to czy się w C# nazywa int , a w innym języku integer to już sprawa kompilatora

1

Po pierwsze int to jest alias nazwy Int32 - taka nazwa zastępcza Też możesz sobie tworzyć takie nawy zastępcze za pomocą słowa kluczowego using.
Po drugie nie wiem czy wiesz czy nie wiesz ale język C# zbudowany z jest z języka niższego rzędu CLI (IL) - Common Intermediate Language.
Ten pośredni język też posiada już takie typy podstawowe jak int32 . Struktura Int32 w C# to jest tak jakby nadbudowa tego int32 w CLI
W końcu ten język CLI zbudowany jest z języka najniższego rzędu - kodu maszynowego zrozumiałego dla procesora komputera .

0

Ok, moj blad. Nie zachodzi autoboxing, bo od razu tworzona jest struktura zamiast "inta". Ale dalej nie wiem, jaki jest tego sens.

6
nobody01 napisał(a):

Czyli chodzi o generyki? Nie da się zrobić, aby generyki obsługiwały jednocześnie obiekty i typy prymitywne takie jak w Javie?

No właśnie Javowe generyki nie obsługują prymitywów, dlatego że prymitywy nie są obiektami. W C# to osiągnięto, bo zarówno struktury, jak i klasy znajdują się w jednej hierarchii dziedziczenia.

No i o co chodzi z tym, że There is no "wrapping" in a struct going on here. i But that happens only if you cast the int to object.?

int to alias na System.Int32, więc tu nie ma żadnego wrapowania, bo to jest po prostu jedno i to samo. Dopiero jak próbujesz go wsadzić do zmiennej typu object, to następuje wrapowanie (a właściwie boxowanie).

nobody01 napisał(a):

Tylko z tego, co rozumiem, to .NET jakoś inaczej traktuje System.Int32 niż zwykłe struktury, aby nie było narzutu z opakowywaniem. Tylko po co tak komplikować?

Nie wiem, o co dokładnie Ci tutaj chodzi, ale ogólna prawda jest taka, że jeśli kompilator zna konkretny typ, to może różne optymalizacje na jego podstawie robić. Normalna sprawa.

0

Z czym macie problem Panowie ? Najlepiej wklejcie kod żebyśmy wiedzieli o czym gadamy

2
nobody01 napisał(a):

Ok, moj blad. Nie zachodzi autoboxing, bo od razu tworzona jest struktura zamiast "inta". Ale dalej nie wiem, jaki jest tego sens.

Nie ma „tworzenia struktury zamiast inta”, tylko jest po prostu int.
W C# taki List<int> to jest lista intów. Nie intów opakowanych w cokolwiek, tylko 32-bitowych intów.

inti System.Int32 to jest to samo. Jedno jest po prostu aliasem drugiego, podobnie jak string i System.String, object i System.Object albo float i System.Single.
W C# każdy „keywordowy” typ ma swoją nazwę „namespace'ową” i obie nazwy są równoważne.

0
somekind napisał(a):
nobody01 napisał(a):

Czyli chodzi o generyki? Nie da się zrobić, aby generyki obsługiwały jednocześnie obiekty i typy prymitywne takie jak w Javie?

No właśnie Javowe generyki nie obsługują prymitywów, dlatego że prymitywy nie są obiektami. W C# to osiągnięto, bo zarówno struktury, jak i klasy znajdują się w jednej hierarchii dziedziczenia.

To jak to z tym dziedziczeniem jest? Wszystko pochodzi od Object ale jak to się dzieje skoro struktury nie mogą dziedziczyć z klas, mogą tylko implementować interfejsy. ?

0

@Azarien: Czyli int jest traktowany jako struktura, mimo ze tak naprawde to po prostu ciag 32 bitow, i to dzieki temu mozliwe jest parametryzowanie generykow typami prostymi, przy jednoczesnym braku narzutu zwiazanego z boxingiem?

2
kzkzg napisał(a):
somekind napisał(a):
nobody01 napisał(a):

Czyli chodzi o generyki? Nie da się zrobić, aby generyki obsługiwały jednocześnie obiekty i typy prymitywne takie jak w Javie?

No właśnie Javowe generyki nie obsługują prymitywów, dlatego że prymitywy nie są obiektami. W C# to osiągnięto, bo zarówno struktury, jak i klasy znajdują się w jednej hierarchii dziedziczenia.

To jak to z tym dziedziczeniem jest? Wszystko pochodzi od Object ale jak to się dzieje skoro struktury nie mogą dziedziczyć z klas, mogą tylko implementować interfejsy. ?

Struktury niejawnie dziedziczą po typie System.ValueType, który dziedziczy po System.Object.

1

Między Object a np. Int32 jest publiczna abstrakcyjna klasa System.ValueType.

Struktura Int32 implementuje 5 interfejsów :IComparable, IComparable<int>, IConvertible, IEquatable<int>, IFormattable.
Ma 2 pola statyczne , jedno pole instancyjne i trochę metod

A to jest dowód że int zajmuje 4 bajty na stosie

using System;
namespace ConsoleApp90
{
    class Program

    {
        static void Main(string[] args)
        {
            unsafe
            {
                int a = 10;
                int b = 20;

                Console.WriteLine("adres zmiennej a {0}", (long)&a);
                Console.WriteLine("adres zmiennej b {0}", (long)&b);   // różnica wynosi 4 bajty 

                Console.WriteLine("Długość adresu w bajtach {0} ", sizeof(int*)); // 4 dla 32 bitów i 8 dla 64 bitów
            }
        }
    }
}
1

w C# są dwie podstawowe kategorie typów: value-type i reference-type. proponuję poczytać o tych pojęciach.

1

I mamy jeszcze typy wskaźnikowe , które nie dziedziczą po System.Object i których można używać tylko w kontekście słowa kluczowego unsafe.- kod niezabezpieczony.
Interfejsy to jest taki twór, który tez nie dziedziczy po System.Object ale jest zaliczany do szeroko rozumianego pojęcia typu

2

No właśnie Javowe generyki nie obsługują prymitywów, dlatego że prymitywy nie są obiektami. W C# to osiągnięto, bo zarówno struktury, jak i klasy znajdują się w jednej hierarchii dziedziczenia.

somekind znawca Javy znowu swoje. Hierarchia typów nie ma tu nic do rzeczy, bo istnieje tylko na etapie kompilacji (w sensie to niby dziedziczenie inta po objekcie), a do generyków na typach wartościowych potrzebne jest wparcie VMki. Javowa VMka praktycznie w ogóle nie wspiera generyków (jedyne wsparcie to zapisywanie w plikach .class informacji o generykach, które są dostępne statycznie w kodzie źródłowym). Dodanie wsparcia dla typów wartościowych i generyków je obsługujących jest w zakresie projektu Valhalla: http://openjdk.java.net/projects/valhalla/

Dziedziczenie po typie object w C# jest pewnego rodzaju iluzją, bo oznacza co najwyżej tyle, że na instancji typu można odpalić metody z klasy object (ToString, HashCode, etc). Jeśli mamy zmienną typu object w rzeczywistości chodzi natomiast o typ referencyjny, a nie dziedziczący po object. Z tego powodu przypisanie inta do zmiennej typu object powoduje boxing (opakowanie zmiennej wartościowej w zmienną referencyjną).

Hierarchia typów to jest coś banalnego do osiągnięcia - twórcy Javy mogliby bez problemu sprawić że 5 instanceof Object zwraca true (w taki sam sposób jak to się dzieje w C#, czyli wbijanie tego true na sztywno do bajtkodu na etapie kompilacji), ale po co, skoro samo w sobie to nic nie daje (a moim zdaniem wprowadziłoby to tylko zamieszanie)?

Iluzję z C# można zaprezentować na wiele sposobów. Kiedyś podawałem przykład narzutu przy wrzucaniu intów do List<object> - inty były opakowywane w typ referencyjny (który lubię nazywać dużym intem). Inny sposób na wykazanie niekompatybilności inta z objectem to np przypisanie tablic. Tablice w Javie i C# są kowariantne (to jeden z błędów Javy, który C# bezmyślnie skopiował), więc tablica podtypów powinna dać się przypisać do zmiennej o typie tablicy nadtypów. Tak się jednak nie dzieje jeśli pomieszamy typy wartościowe z referencyjnymi: https://www.ideone.com/cJgMgT

using System;
 
public class Test
{
	public static void Main()
	{
		object[] objects = null;
		int[] ints = null;
		string[] strings = null;
 
		// this works because arrays are covariant
		objects = strings;
		// this doesn't work because we're mixing reference types with value types
		objects = ints;
	}
}
0

Jak rzutujemy strukturę (pakujemy) na System.Object to tworzony jest nowy obiekt na zarządzanej stercie . Dla jednego int-a jest tworzony obiekt wielkości 24 bajtów w trybie 64 bit.
Czyli jest to mało opłacalne z kilku powodów . Niepotrzebne zużycie pamięci ii procesora .
Z tego właśnie powodu powstały typy generyczne.

using System;
namespace ConsoleApp
{
   
    class Program

    {
        static void Main(string[] args)
        {
            X struc = new X();
            int intVar = 20;

            object ob1 = (object)struc; // 24  albo 12 bajtów w zalezności czy program działa w 32 czy 64 bitach
            object ob2 = (object)intVar; // 24 albo 12 bajtów
  
        }
 
    }

    struct X
    {
        public int a;
    }
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1