Czy polecilibyście naukę C# dla javovca ?

0

Int jest obiektem, w C#, i używając go nie strzelisz sobie w stopę, a w javie możesz.

0
Wibowit napisał(a):

5.GetType() już nie jest przepisywane na dobieranie się do klasy System.Int32 wprost, chociaż mogłoby być i wtedy też uniknęlibyśmy niepotrzebnego boxingu.

tyle ze ciezko wymyslic scenariusz w ktorym wywolywanie GetType() na value type mialo jakikolwiek sens

0

@Pixello:
Moim zdaniem jest odwrotnie. Rozróżnienie między intem, a Integerem w Javie jest jasne i przewidywalne. Brak możliwości użycia prymitywów w genericsach w Javie też jest oczywisty (ale czasami bolesny). Natomiast w C# można się naciąć rzutując inta na object: https://stackoverflow.com/questions/6205029/comparing-boxed-value-types Unified view w C# jest nieco cieknącą abstrakcją.

1

A wracając do meritum, nie poleciłbym C# (chyba że z ciekawości dla zabicia czasu, ew. większego pokrycia ofert enterprise).
C# vs Java to po prostu inna składnia, ale to w zasadzie te same języki. No dobra, w C# masz metody z dużej litery.
Zresztą na JVM jest kilka innych języków w których czasami można dostać robotę, jak już się Java znudzi.

Dla przypomnienia:

  • Scala (184 aktywne oferty na indeed.com)
  • Groovy / Grails (62 oferty)
  • Kotlin / Android (19 ofert)
  • Clojure (4 oferty)
  • Jython (4 oferty)

Więcej: http://www.oracle.com/technetwork/articles/java/architect-languages-2266279.html

Dla porównania:

  • Java developer: 2200 ofert
  • C# developer: 923 oferty
  • F# developer: 12 ofert
  • Delphi developer: 5 ofert

Osobiście to poleciłbym jakiś język spoza JVM/.NET (jeśli nie znasz), ale to już zupełnie inna historia.

0

@Wibowit:

using System;
					
public class Program
{
	public static void Main()
	{
		Console.WriteLine("Hello World");
		int i =5;
		object o = i;
		Console.WriteLine(o.GetType());
	}
}

Hello World
System.Int32
https://dotnetfiddle.net/

wut, zawsze Object::GetType() zwraca tak zwany Runtime Type.

5
Wibowit napisał(a):

Rozróżnienie między intem, a Integerem w Javie jest jasne i przewidywalne.

dobry zart. zwlaszcza niejawna konwersja jest taka oczywista ze no nie ma szans zeby sie zaskoczyc :) albo dzialanie equals vs ==

Natomiast w C# można się naciąć rzutując inta na object

nie zdarzylo mi sie takie naciecie przez 10 lat kodowania w c#, jak ktos cuduje zamiast nauczyc sie genericsow to tak wlasnie sie dzieje

0

dzialanie equals vs ==

equals to metoda jak każda inna, a == porównuje zawartość zmiennych (prymitywów bądź referencji). Trzeba poczytać o podstawach, zamiast wyrabiać sobie błędne intuicje na podstawie kilku nieprzemyślanych eksperymentów.

0

Czy polecilibyście naukę C# dla javovca ?

Nie można polecić „dla kogoś”, poleca się komuś. A przed znakiem zapytania nie stawia się spacji.

0

Głupota tej dyskusji jest tak kuriozalna, że można ją chyba tylko porównać do wynaturzeń ekspertów smoleńskich o parówkach i generatorach mgły. Nie rozumiem, jak można być tak pozbawionym wyobraźni, żeby nie rozumieć, że jakiś język może mieć inny system typów niż Java.
Przecież to tak jakby twierdzić, że C++ nie może mieć wielodziedziczenia, bo Java nie ma, albo PHP nie może być dynamicznie typowany, bo Java nie ma typu dynamicznego. :|

Naprawdę nie wiem, nad czym można tyle deliberować, wszystko istotne jest opisane w dokumentacji:

C# has a unified type system. All C# types, including primitive types such as int and double, inherit from a single root object type.
https://docs.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/

Reference types are heap-allocated. Both reference and value types are derived from the ultimate base class Object.
https://msdn.microsoft.com/en-us/library/windows/desktop/system.valuetype(v=vs.110).aspx

Opis Int32 (hierarchia dziedziczenia zaraz na początku):
https://docs.microsoft.com/en-us/dotnet/api/system.int32?view=netframework-4.7

Mapowanie słów kluczowych C# na typy .NET (w razie, jakby ktoś nie wiedział, że int == System.Int32):
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/built-in-types-table

0

No dobra, biję się w pierś - zrobiłem pewne założenie, które zmienia argumentację. Somekind tak mocno podkreślał to, że w C# int dziedziczy po object'u, że założyłem iż nie chodzi o garść (co najwyżej średnio przydatnego) lukru składniowego tylko o coś co przeżywa etap kompilacji i jest obecne w MSILu/ CLRze, wpływa pozytywnie na wydajność, itd. Tymczasem to dziedziczenie inta po obejct'u istnieje tylko w czasie kompilacji (jak genericsy w Javie), natomiast w czasie wykonania (tudzież: na poziomie bajtkodu) typy wartościowe nie mają nic wspólnego z objectem. Pewne konstrukcje na intach są wyifowane w kompilatorze, jak np wspomniane instancjaTypu wartościowego is object jest przepisywane na etapie kompilacji wprost na true, wywołania typu 5.ToString() są zastępowane wczesnym wiązaniem, a tam gdzie rzeczywiście potrzebny jest object to typy wartościowe są boxowane do obiektu - tym samym typy wartościowe i object są zupełnie inaczej traktowane w MSILu/ CLRze.

Scala także ma unified view w czasie kompilacji - identycznie jak C# (z tą różnicą, że w Scali typem z którego dziedziczą wszystkie inne jest scala.Any, a nie jakiś 'object', no ale to szczegół). Nikt jednak z tego powodu napinki nie robi. Ja sam też nie odczułem specjalnie jakiegoś zysku z unified view. To raczej ciekawostka.

0
Wibowit napisał(a):

Trzeba poczytać o podstawach, zamiast wyrabiać sobie błędne intuicje na podstawie kilku nieprzemyślanych eksperymentów.

Wibowit napisał(a):

No dobra, biję się w pierś - zrobiłem pewne założenie, które zmienia argumentację. Somekind tak mocno podkreślał to, że w C# int dziedziczy po object'u, że założyłem iż nie chodzi o garść (co najwyżej średnio przydatnego) lukru składniowego tylko o coś co przeżywa etap kompilacji i jest obecne w MSILu/ CLRze, wpływa pozytywnie na wydajność, itd. Tymczasem to dziedziczenie inta po obejct'u istnieje tylko w czasie kompilacji (jak genericsy w Javie), natomiast w czasie wykonania (tudzież: na poziomie bajtkodu) typy wartościowe nie mają nic wspólnego z objectem.

To nie jest tylko cukier składniowy, ale do tego trzeba wgłębić się w szczegóły implementacyjne, takie jak dwa wpisy do jednej metody w tablicy metod dla typu wartościowego, gdzie jeden wpis jest dla opakowanej wartości, a drugi dla gołej.

Wibowit napisał(a):

Pewne konstrukcje na intach są wyifowane w kompilatorze, jak np wspomniane instancjaTypu wartościowego is object jest przepisywane na etapie kompilacji wprost na true

Zaskoczę Cię: wszystko dziedziczy po object, więc to „wyifowanie” zadziała również dla typów referencyjnych.

Wibowit napisał(a):

wywołania typu 5.ToString() są zastępowane wczesnym wiązaniem

To raczej logiczne, że jeżeli znamy typ i wiemy w trakcie kompilacji, co ma się wywołać, to wiążemy to wcześnie.

Wibowit napisał(a):

a tam gdzie rzeczywiście potrzebny jest object to typy wartościowe są boxowane do obiektu - tym samym typy wartościowe i object są zupełnie inaczej traktowane w MSILu/ CLRze.

No to handluj z tym:
https://dotnetfiddle.net/oysRbV
https://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.constrained(v=vs.110).aspx

Spójrz na boxowanie dla operatora is, a potem na wywołanie metody ToString.

Jakkolwiek by nie było, to dyskusja czysto akademicka, bo dla jednego boxowanie będzie specjalnym traktowaniem, dla innego nie będzie, bo jest niespecjalnie widoczne w kodzie źródłowym.

0

Zaskoczę Cię: wszystko dziedziczy po object, więc to „wyifowanie” zadziała również dla typów referencyjnych.

Nie. Dla typów referencyjnych przepisywane jest do innej postaci. referencja is object jest przepisywana na referencja != null.

No to handluj z tym:
https://dotnetfiddle.net/oysRbV
https://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.constrained(v=vs.110).aspx

Spójrz na boxowanie dla operatora is, a potem na wywołanie metody ToString.

No to tutaj narzut będzie większy, bo w szablonie jak widać się nie da wyifować isa bez boxowania.

A ToString będzie działać różnie dla typów wartościowych i referencyjnych. To wynika z dokumentacji, którą sam podałeś.

To raczej logiczne, że jeżeli znamy typ i wiemy w trakcie kompilacji, co ma się wywołać, to wiążemy to wcześnie.

Na mój gust to raczej powodem jest ułomność JITa w .NETu. Jeśli porównamy JVM vs CLR to okazuje się, że do osiągnięcia dobrej wydajności CLR wymaga ręcznych optymalizacji (rodem z C++), podczas gdy JVM świetnie sobie radzi sobie z wycinaniem rozsądnych abstrakcji.

Dla przykładu tutaj https://github.com/dotnet/coreclr/issues/9105 jest opisane jak użycie interfejsu (IList) zamiast docelowej implementacji (np List) zmniejsza wydajność 10x (w przypadku Javy spadku wydajności nie ma). Sam interfejs jest trywialny, a CLR w roku 2017 się od razu poddaje - a w zasadzie to nie implementuje dewirtualizacji metod, chociaż owa dewirtualizacja w świecie Javy jest standardem od pewnie parunastu lat.

Kiepsko ten CLR wygląda skoro użycie naturalnych obiektowych abstrakcji (rodem z np SOLIDa) skutkuje mocnym spadkiem wydajności. Podczas pracy w biurze słyszałem nawet jak jeden gość z zespołu .NOTowców (którzy siedzą obok) wprost krytykował użycie chyba foreach, bo w .NETu foreach działa wolno. Brawo!

Microsoft jednak zorientował się, że jest mocno do tyłu ze swoją VMką i zaczął eksperymentowanie z dewirtualizacją metod. Stworzył eksperymentalny JIT o nazwie RyuJIT i wstawił w niego podstawowe optymalizacje: https://blogs.msdn.microsoft.com/dotnet/2017/06/29/performance-improvements-in-ryujit-in-net-core-and-net-framework/

0
Wibowit napisał(a):

Zaskoczę Cię: wszystko dziedziczy po object, więc to „wyifowanie” zadziała również dla typów referencyjnych.

Nie. Dla typów referencyjnych przepisywane jest do innej postaci. referencja is object jest przepisywana na referencja != null.

No skoro wiesz lepiej od kompilatora i ILSpy'a, to ta dyskusja nie ma sensu.

A ToString będzie działać różnie dla typów wartościowych i referencyjnych. To wynika z dokumentacji, którą sam podałeś.

Trudno, żeby działało jednakowo, skoro wewnętrzna struktura jest inna. To tak, jakby wykłócać się, że double nie jest liczbą, bo jest wypisywany inaczej, niż int. Patrz na kod IL-owy, który nie robi żadnego boxowania.

Na mój gust to raczej powodem jest ułomność JITa w .NETu. Jeśli porównamy JVM vs CLR to okazuje się, że do osiągnięcia dobrej wydajności CLR wymaga ręcznych optymalizacji (rodem z C++), podczas gdy JVM świetnie sobie radzi sobie z wycinaniem rozsądnych abstrakcji.

Ten komentarz (i wszystkie następne) nie ma żadnego znaczenia w dyskusji o tym, czy int jest obiektem. JIT w .NET w istocie jest ubogi i czasami przy HotSpocie wygląda jak zabawka, ale to bez znaczenia.

0

No skoro wiesz lepiej od kompilatora i ILSpy'a, to ta dyskusja nie ma sensu.

Przed napisaniem sprawdziłem co wypluwa ILSpy. Taki kod w C#:

	public static void Main()
	{
		Console.WriteLine(isObject("Hello World"));
	}
	
	public static bool isObject(object x)
	{
		return x is object;
	}

daje taki bajtkod:

  .method public hidebysig static void  Main() cil managed
  {
    // 
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldstr      "Hello World"
    IL_0006:  call       bool Program::isObject(object)
    IL_000b:  call       void [mscorlib]System.Console::WriteLine(bool)
    IL_0010:  nop
    IL_0011:  ret
  } // end of method Program::Main

  .method public hidebysig static bool  isObject(object x) cil managed
  {
    // 
    .maxstack  2
    .locals init (bool V_0)
    IL_0000:  nop
    IL_0001:  ldarg.0
    IL_0002:  ldnull
    IL_0003:  ceq
    IL_0005:  ldc.i4.0
    IL_0006:  ceq
    IL_0008:  stloc.0
    IL_0009:  br.s       IL_000b

    IL_000b:  ldloc.0
    IL_000c:  ret
  } // end of method Program::isObject

Wychodzi na to że na poziomie bajtkodu bool jest liczbą, a metoda isObject wygląda tak:
(x == null) == 0

https://dotnetfiddle.net/vUBbBq

using System;
					
public class Program
{
	public static void Main()
	{
		Console.WriteLine(isObject("Hello World"));
		Console.WriteLine(isObject(null));
	}
	
	public static bool isObject(object x)
	{
		return x is object;
	}
}

Wyjście:

True
False
0
Wibowit napisał(a):

No dobra, biję się w pierś - zrobiłem pewne założenie, które zmienia argumentację. Somekind tak mocno podkreślał to, że w C# int dziedziczy po object'u, że założyłem iż nie chodzi o garść (co najwyżej średnio przydatnego) lukru składniowego tylko o coś co przeżywa etap kompilacji i jest obecne w MSILu/ CLRze, wpływa pozytywnie na wydajność, itd. Tymczasem to dziedziczenie inta po obejct'u istnieje tylko w czasie kompilacji (jak genericsy w Javie),

Ja od początku pisałem wyłącznie o systemie typów .NET, co jest istotne dla pisanego kodu, nie o szczegółach implementacji tego w maszynie wirtualnej. Liczy się to, że w kodzie jest jeden twór System.Int32 będący częścią wspólnego systemu typów, może być używany jako parametr generyczny, który jest po prostu normalnym typem z hierarchii. Nie ma tej dychotomii obecnej np. w Javie.
A słowo kluczowe int istnieje głównie dlatego, że C# w założeniach miał być trochę podobny do C++, aby programiści mogli się przenieść.

natomiast w czasie wykonania (tudzież: na poziomie bajtkodu) typy wartościowe nie mają nic wspólnego z objectem.

To też raczej nie jest nic dziwnego, zwłaszcza w przypadku typów liczbowych. To w końcu tylko bajty w pamięci, o znaczeniu określonym przez standard.

No i taki szczegół, że .NET pozwala też na definiowanie własnych typów wartościowych, a Java nie. A to w niektórych przypadkach może być przydatne.

Wibowit napisał(a):

Kiepsko ten CLR wygląda skoro użycie naturalnych obiektowych abstrakcji (rodem z np SOLIDa) skutkuje mocnym spadkiem wydajności.

Tak, maszyna JVM jest dojrzalsza i większości wypadków wydajniejsza niż CLR, to chyba nie ulega wątpliwości.

Podczas pracy w biurze słyszałem nawet jak jeden gość z zespołu .NOTowców (którzy siedzą obok) wprost krytykował użycie chyba foreach, bo w .NETu foreach działa wolno. Brawo!

Ale co mu konkretnie działa wolno? Wolno w porównaniu z czym? Bo tak twierdzą głównie newbie, którzy wiedzę czerpią z blogów sprzed 10-15 lat, albo po prostu nie potrafią czegoś używać.

1
Wibowit napisał(a):

Przed napisaniem sprawdziłem co wypluwa ILSpy. Taki kod w C#:

No i to jest zupełnie inna sytuacja, niż mieliśmy wcześniej. Zauważ, że piątka nigdy nie będzie nullem i stąd kompilator wie, że zawsze będzie obiektem, w Twoim kodzie obecnie może być null, wiec trzeba to sprawdzić inaczej. Zerknij nawet na taki banalny przykład https://dotnetfiddle.net/eqrTrt albo na porównanie wywołań różnych metod i co kompilator robi, gdy ma pewność, że nulla nie będzie (uwaga, autopromocja) https://blog.adamfurmanek.pl/2016/05/21/virtual-and-non-virtual-calls-in-c/

Pozwolę sobie odwrócić pytanie: jak musiałby działać int, żebyś uznał, że w istocie jest obiektem? Ciągle odbijasz oficjalną dokumentację i podane przykłady potwierdzające to stwierdzenie, więc napisz proszę, jak to należy zaimplementować.

0

Ale co mu konkretnie działa wolno? Wolno w porównaniu z czym? Bo tak twierdzą głównie newbie, którzy wiedzę czerpią z blogów sprzed 10-15 lat, albo po prostu nie potrafią czegoś używać.

Usłyszałem tylko w rozmowie, że gość ma chęć przepisywać foreach na zwykły for. Sprawdziłem teraz sam i różnice są, ale małe: http://ideone.com/DQHd6S

Liczy się to, że w kodzie jest jeden twór System.Int32 będący częścią wspólnego systemu typów, może być używany jako parametr generyczny, który jest po prostu normalnym typem z hierarchii. Nie ma tej dychotomii obecnej np. w Javie.
(...)
No i taki szczegół, że .NET pozwala też na definiowanie własnych typów wartościowych, a Java nie. A to w niektórych przypadkach może być przydatne.

Parametryzowanie Listy (bądź jakiejkolwiek innej klasy czy metody generycznej) małym intem nie ma nic wspólnego z udawaniem obiektu przez małego inta. W Scali Int dziedziczy po scala.Any i można nimi parametryzować metody bądź klasy, ale na poziomie bajtkodu typy są i tak wymazywane i jest odpalany autoboxing.

Trwają prace nad wprowadzeniem typów wartościowych do Javy oraz na umożliwienie parametryzowania klas i metod typami wartościowymi - odbywa się to pod Projektem Valhalla: http://openjdk.java.net/projects/valhalla/ Chciałem podać coś konkretnego, ale niestety ichniejsza wiki teraz zamula i nic nie da się pokazać.

W każdym razie propozycja typów wartościowych i parametryzowania nimi klas i metod wygląda tak:

  • typy wartościowe nie udają typu java.lang.Object w żaden sposób - próba użycia typu wartościowego jako obiektu (o ile się skompiluje) kończy się boxowaniem (czyli tak jak jest teraz)
  • genericsy dla typów referencyjnych dalej opierają się na wymazywaniu typów - jest zachowana kompatybilność wsteczna
  • genericsy dla typów wartościowych (w tym małego inta) zachowują się jak szablony/ specjalizacje
  • jeżeli chcemy by w miejscu danego parametru generycznego móc użyć typu wartościowego to trzeba dopisać słowo kluczowe 'any' przed tym typem. A więc np zamiast class A<T> {} trzeba napisać class A<any T> {} - stary kod generyczny nie uzyska więc automatycznie wparcia dla parametryzowania typami wartościowymi i trzeba będzie zmodyfikować kod
  • można (ale nie trzeba) stworzyć różne implementacje dla typów referencyjnych i wartościowych, np w Optionalu dla typów referencyjnych używamy nulla do oznaczenia pustego Optionala, a dla typów wartościowych używamy flagi (booleana)

Aktualizacja: znalazłem stronę która nie zamula, ale to jest wersja propozycji sprzed 3 lat: http://cr.openjdk.java.net/~briangoetz/valhalla/specialization.html

Pozwolę sobie odwrócić pytanie: jak musiałby działać int, żebyś uznał, że w istocie jest obiektem? Ciągle odbijasz oficjalną dokumentację i podane przykłady potwierdzające to stwierdzenie, więc napisz proszę, jak to należy zaimplementować.

Już przyznałem się do błędu kilka postów wcześniej. Powtórzę jeszcze raz: założyłem, że int nie udaje tylko obiektu na etapie kompilacji, ale że typ object jest nadtypem typów wartościowych również na poziomie bajtkodu/ etapie wykonania. Zorientowałem się jednak, że somekindowi chodziło tylko o udawanie obiektu na etapie kompilacji.

A jak musiałby działać int by można go było uznać za obiekt również na poziomie bajtkodu? No, musiałby działać tak jak inne obiekty, czyli przypisanie nieopakowanego inta do pola typu object musiałoby się odbywać bez boxowania. Bo po co pakować obiekt w obiekt?

0

@Wibowit: Widzisz jaki ten C# nieefektywny?
Dorzucę nawet coś swojego.

W generykach nawet <Q> where Q : class jest boxowane przed przypisaniem do pola typu interfejsu. Co lepsze nawet z constrainem typu tego interfejsu

using System;
 
public class Program
{
    public IDisposable Disposable;
    public void Method<T>(T t)
        where T : class, IDisposable
    {
        this.Disposable = t;
    }
}
   IL_0000: ldarg.0      // this
    IL_0001: ldarg.1      // t
    IL_0002: box          !!0/*T*/
    IL_0007: stfld        class [mscorlib/*23000001*/]System.IDisposable Program/*02000002*/::Disposable/*04000001*/
    IL_000c: ret     

Olaboga, ten C# to jakiś lewy, nawet typy referencyjne są traktowane jak nie, od jutra piszę w javie ;)

0

To jest szablon. W momencie konkretyzowania szablonu dla konkretnego zestawu parametrów instrukcje box mogą być bez problemu przepisane na nop. Niczego tym kodem nie udowodniłeś.

Aktualizacja:
Specyfikacja CLI/ CIL mówi wprost, że boxowanie obiektu nic nie robi.
https://stackoverflow.com/q/20683715
pdf ze specyfikacją

If typeTok is a value type, the box instruction converts val to its boxed form. When typeTok is a non-nullable
type (§I.8.2.4), this is done by creating a new object and copying the data from val into the newly allocated
object. If it is a nullable type, this is done by inspecting val‘s HasValue property; if it is false, a null reference is
pushed onto the stack; otherwise, the result of boxing val‘s Value property is pushed onto the stack.

If typeTok is a reference type, the box instruction does returns val unchanged as obj.

If typeTok is a generic parameter, the behavior of box instruction depends on the actual type at runtime. If this
type is a value type it is boxed as above, if it is a reference type then val is not changed. However the type
tracked by verification is always "boxed" typeTok for generic parameters, regardless of whether the actual type
at runtime is a value or reference type.

Widocznie łatwiej było wycinać niepotrzebny boxing na etapie wykonania niż na etapie kompilacji.

0

Dla Javowca to Kotlin, Ceylon, Scala nie widzę sensu pchać się w C#. JVM wspiera dużo więcej nowych języków programowania i daje dużo więcej możliwości.

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