Jump to content
Unity Insider Forum

C# für Umsteiger, Einsteiger, Bergsteiger


Hendrik

Recommended Posts

Hallo Leute!

Ich will mit diesem kleinen Tutorial es den Umsteigern von Javascript auf C# ein wenig leichter machen, vorallem Jenen die mit dem C# Compiler auf Kriegsfuß stehen.

Dabei werde ich nicht auf Vorteile oder Nachteile zwischen C# und Javascript eingehen, beide Sprachen haben durchaus ihre Daseinsberechtigung und sind gleichermaßen schnell (Um mit dem Vorurteil "C# ist schneller" aufzuräumen).

 

Beginnen wir mit dem, womit auch das Scripting Tutorial von Sascha anfängt: Variablen.

 

Grundsätzlich wird in C# typenstark programmiert. Das heißt im Klartext, das jede Variable bei ihrer Deklaration einen festgelegten Datentyp hat.

Klamotten wie

 

var myString = "Hallo, das ist mein String!"; //Eine Variable vom Typ String
var myInteger = 5; //Eine Variable vom Typ Int (Integer)

 

sind in C# schlicht nicht möglich, weil "is nicht".

Der Compiler weiß mit dem Datentyp "var" nichts anzufangen. Weil der Compiler nicht schlau genug ist, nach der Zuweisung den Variabletyp zu erfassen, meckert er was das Zeug hält.

(Für alle Computernerds: Ja ich weiß, das hat mit etwas ganz anderem zu tun, mal ganz davon abgesehen dass Compiler nicht soetwas wie eine Intelligenz besitzen. Trotzdem sehe ich hier von einer ausschweifenden Erklärung ab.)

C# Programmierer müssen also etwas kleinkarierter sein was Datentypen angeht. Der gleiche Code wie oben müsste in C# etwa so aussehen:

 

String myString = "Hallo, das ist mein String!"; //Eine Variable vom Typ String
Int myInteger = 5; //Eine Variable vom Typ Int (Integer)

 

Der Compiler weiß sofort, welche Datentypen er nehmen soll (Wieviel Speicher er zur Laufzeit reservieren soll) und ist zufrieden.

 

Zudem ist der Compiler in der automatischen Typenkonvertierung nicht bewand. Hinter diesem zugegeben etwas kryptischen Begriff (Wer hat sich sowas ausgedacht!?) steht ein simples Prinzip:

 


function Start()
{
var myFloat : float = 5.8453232453f;
print("myFloat:" + myFloat);
myFunction(myFloat);
}


function myFunction(myInt : int )
{
var myIntIntern : int = myInt;
print("myIntIntern:" + myIntIntern);
}

 

Hierbei meckert der Compiler nicht. Wer sich allerdings etwas mit Datentypen auskennt (Und etwas in der Programmierung auf sich hält), dem stellen sich bei diesem Code schlagartig die Nackenhaare hoch. Denn wenn ihr diesen Code laufen lässt, werdet ihr 2 Ausgaben bekommen:

 

myFloat:5.845323

 

und

 

myIntIntern:5

 

Was stellt ihr fest? Aus 5.845323 ist auf einmal 5 geworden. Das liegt an den verschiedenen Typen, mit denen in der Funktion myFunction gearbeitet wird und die myFunction dann wirklich übergeben bekommt. myFunction erwartet einen Integer, während wir eine Float-Variable übergeben, die Funktion bekommt also mehr Daten übergeben als sie wirklich frisst. Der C# Compiler würde hier ganz klar meckern und den Code partou nicht kompilieren wollen (Argument `#1' cannot convert `float' expression to type `int' mimimi). Und das auch aus gutem Grund: Der Javascript Compiler merkt nämlich, das wir eine Float Variable übergeben, während die Funktion eine Integer Variable erwartet. Hilfsbereit wie der nette Compiler ist, wandelt er unsere Float Variable in einen Integer um.

Jeder wird hier nach ein wenig Schmökern in einem beliebigen Programmierhandbuch(Es steht in jedem, das ich bisher gelesen hab!) feststellen, dass da ein Haken ist: int kann keine Nachkommastellen darstellen, float schon. Und so schneidet die CPU unserer float Variable fröhlich pfeifend die Nachkommastellen ab. Das ist in den meisten Fällen nicht beabsichtigt, wir wollen ja möglichst genau Ergebnisse. Deswegen wird in C# eine explizite Typenumwandlung, ein sogenannter "Cast" erwartet. Hier der Code, mit dem der C# Compiler keine Zicken macht:

 

 

public class Test : MonoBehaviour
{
void Start()
{
float myFloat = 5.8453232453f;
print("myFloat:" + myFloat);
myFunction((int)myFloat);
//Ein Cast wird mithilfe des gewünschten Typs umschlossen von Klammer erstellt
}


void myFunction(int myInt)
{
int myIntIntern  = myInt;
print("myIntIntern:" + myIntIntern);
}
}

 

Eine kleine Anmerkung (Die wahrscheinlich SEHR wichtig ist): Wenn ihr Variablen in C# ohne explizite Zugriffsberechtigung (Protection Level) deklariert, sind sie automatisch

private

, nicht wie in Javascript

public

!

 

Fast beiläufig habe ich euch in diesem kleinen Beispiel eine weitere änderung untergeschmuggelt (Muhaha!):

Funktionen haben in C# eine andere Syntax.

Während in Javascript Funktionen folgendermaßen deklariert werden:

 

function machWasMeineGueteIstDasVonSaschasBeispielenGeklaut()
{
}

function machWasGeklautUndMitRueckgabe() : int
{
return 1;
}

 

Sieht die ganze Klamotte in C# etwas anders aus:

 

void machWasMeineGueteIstDasVonSaschasBeispielenGeklaut()
{
}

int machWasGeklautUndMitRueckgabe()
{
return 1;
}

 

Generell gilt: In C# wird der Rückgabewert einer Funktion immer vor den Funktionsnamen geschrieben, sonst gibts ärger vom Compiler(Und davor kommt natürlich die Zugriffsberechtigung!).

Für alle armen Tropfe unter euch, die sich jetzt mit Strick in der Hand fragen, wie sie denn zum Teufel eine Funktion ohne Rückgabewert deklarieren sollen:

Das Schlüsselwort heißt "void". Void ist English (You don´t say?!), und bedeutet soviel wie "Leere" oder einfach "Nix" ("Nichts" für unsere ältere Generation). Also für Funktionen ohne Rückgabewert einfach ein "void" vor den Funktionsnamen klatschen und der Compiler freut sich über eine schön eingehaltene Syntax.

 

Praxis in C#

Oder: Wieviele Haare kann ich mir ausreißen, bis ich keine mehr habe?

 

Jaja, jeder wird beim Umsteigen von JavaScript erstmal verzweifeln. Spätestens wenn das erste Script von JavaScript auf C# umschrieben wird, ist der Motivationspegel etwa so hoch, wie Politiker gute Absichten haben. (Seid ehrlich!) Es gibt da nämlich einige Feinheiten, die JavaScript einem abnimmt, C# aber eben nicht. Dazu gehört unter anderem die automatische Typenkonvertierung, die es in C# nicht gibt (Casten, Casten, Casten und nochmals Casten!) als auf einige Feinheiten bei Wertveränderungen der eingebauten Unity-Komponenten.

 

1. Stolperfalle

 

Ich bin mal so frech zu behaupten, dass dieser Fehler einem Jeden Neuankömmling in den ersten 10-15 Minuten begegnen wird:

 

post-847-0-54223500-1329680471_thumb.png

 

Was zum... das hat doch in JavaScript immer funktioniert?

Wovon ich rede ist folgender Code: (Ja ich weiß, unglaublich einfallsreich..)

 

void Start () 
{
 transform.position.y +=1;
}

 

Der Fehler liegt bei der direkten Wertzuweisung von transform.position.y. Da Vector3 in Unity ein Struct ist (Mehr dazu im Klassentutorial) , und Structs in C# etwas stiefmütterlich behandelt werden, kann man auf Struct-Member nicht über 2 Schichten zugreifen (1: transform, 2:position).

Wie Blöde...

Also nur über eine Schicht, und den ganzen Vector neu zuweisen.

Wir ändern ab:

 

transform.position = new Vector3(transform.position.x, transform.position.y+1, transform.position.z);

Traumhaft. Kein Fehler mehr. Aber auch mehr Code, und vor allem: etwas unübersichtlich. Beschwert euch da aber lieber bei Microsoft, denn die haben C# erfunden.

 

2.Stolperfalle

 

Schauen wir uns dieses JavaScript an:

 

var myFloat :float;

function Start () 
{
 print(myFloat);
}

Super, alles paletti. Ist doch easy das ganze auf C# umzuschreiben:

 

 

float myFloat;

void Start () 
{
 print(myFloat);
}

 

Folgendes Problem: Wenn ich das Script einem Objekt anhänge, wird kein Bearbeitungsfeld angezeigt, whaaaaat?

Ups, da hat aber Jemand was vergessen: Was ist der Standard-Zugriffsmodifizierer in C#?

Richtig, private!

Deshalb: Wenn ich eine Variable im Editor modifizieren will, schön public davor schreiben.

Protipp:

Es gibt auch das [serializeField] Flag, damit kann man z.B. private Variablen im Inspector modifizierbar machen:

[serializeField]
float myFloat;

 

3.Stolperfalle

Das Ding mit den Floats. In JavaScript, wäre eine Zuweisung wie

 

myFloat = 32.43;

absolut okay. In C# nicht.

ERROR:Literal of type double cannot be implicitly converted to type `float'. Add suffix `f' to create a literal of this type

 

Wichtig ist, das der C# Compiler Werte wie 4.3232 oder auch 454.5422 immer als double erkennt, weil double eine höhere Priorität hat als float (ist halt genauer, und im Zweifel lieber genauer als langsamer)

Deshalb muss das Suffix "f" angehängt werden. Das zeigt dem Compiler ganz genau, dass diese Variable vom Typ float ist.

 

/*TODO: Add more Stolperfallen*/

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich finde das Tutorial gut,eigentlich.

Das Problem : der Titel.

Für Auftseiger-ich hasse es wenn C#-Programmierer sagen das man nur wirklich gut ist,

wenn man mit C# anstatt Javscript programmiert.Und das jenes einfach falsch ist,

weißt du auch(trau ich dir zu).Weil der Syntax von JS einfacher ist wird es einfach

als Anfängersprache abgestempelt.Ich glaube das ich inzwischen nicht mehr zu den

Anfängern gehöre und ich programmiere mit Javascript weil es für mich einfach

klarer und strukturierter als C# ist.

 

PS : Ich kann genauso gut C# und der Kommentar ist nicht persönlich gemeint :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 2 weeks later...

Hi,

 

nettes kleines Tutorial, aber bei dem Teil zu "var" möchte ich noch etwas ergänzen:

 

Den "Datentyp" var gibt es in C# schon, weitere Informationen gibt es hier. Allerdings würde ich persönlich von der Nutzung abraten, da es ohne var immer übersichtlicher ist und man weiß welchen Datentyp die Variable besitzt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 2 weeks later...
  • 2 months later...

Das var Keyword ist nur Syntactic sugar und ich würde empfehlen es zu benutzen, wozu 2x den Typü angeben wenn es einmal reicht, absolute Anfänger werden damit allerhöchstens Probleme haben, aber diesen Status bringt man so schnell hinter sich dass es sich schon gar nicht lohnt etwas anderes zu lernen:

 

Kleines Beispiel:

var myFloat = 3.14f;
float myFloat = 3.14f;

Den f Prefix sollte man kennen wenn man mit Zahlen hantiert, er besagt eindeutig dass die Zahl ein float ist daher ist die Angabe des Typs float recht unnötig. Das in diesem Beispiel die Variable sogar myFloat heißt macht es sogar noch viel ersichtlicher. Gut myFloat oder sonstige ähnliche Bezeichnungen welche an die schreckliche ungarische Notation angelehnt sind sollte man natürlich nicht benutzen. Vermutlich hat man eher so etwas hier:

 

var PI = 3.14f;
float PI = 3.14f;

PI sollte jeder kennen und jeder sollte wissen dass es eine Zahl ist und kein String.

 

var kann einem vieles erleichtern und sollte deswegen nicht einfach missachtet werden, nutzt die Sprachfeatures, sie sind nicht da um euch zu ärgern.

 

Und ja man kann in 100% aller Fälle ohne var auskommen, aber nur weil man es kann heißt es nicht das man es muss ;)

 

Ist nur meine persönliche Meinung dazu.

 

Zu den structs und dem Teil hier:

 

Was zum... das hat doch in JavaScript immer funktioniert?

Wovon ich rede ist folgender Code: (Ja ich weiß, unglaublich einfallsreich..)

void Start ()
{
 transform.position.y +=1;
}

Der Fehler liegt bei der direkten Wertzuweisung von transform.position.y. Da Vector3 in Unity ein Struct ist (Mehr dazu im Klassentutorial) , und Structs in C# etwas stiefmütterlich behandelt werden, kann man auf Struct-Member nicht über 2 Schichten zugreifen (1: transform, 2:position).

 

C# macht genau das was man erwartet, UnityScript hat hier einfach nur Syntactic sugar benutzt, intern muss es genauso ablaufen wie in C#. Woran liegt das? structs sind value types, sprich es werden keine Referenzen auf die Instanzen hergegeben (ref und out keywords benutzen sind was anderes) sondern immer nur Kopien, das ist behind the scenes auch in US so.

 

Was zB wird aus dem hier:

 

US:

transform.position.x += 10;
transform.position.y += g * Time.deltaTime;

Nämlich das:

Vector3 tmp = transform.position;
tmp.x += 10;
transform.position = tmp;
tmp = transform.position;
tmp.y += g * Time.deltaTime;
transform.position = tmp;

 

Wenn man nun herumbitche möchte kann man noch erwähnen dass dies im schlechtesten Fall zu ganzen:

- 4x GetComponent<Transform>()

- 6x Vector3 Construktoren wird (optimiert werden es weniger sein)

 

Im grunde recht praktisch von US dies zu verstecken, viele mögen es sicher so, nur wird damit einiges an Ablauflogik versteckt, deswegen halte ich diese Variante für sicherer:

transform.position += new Vector3(0, 10, g * Time.delta);

 

Ansonsten ist es ein schönes Tutorial, mir wäre aber ein etwas neutralerer Ton lieber gewesen, nicht dass das ganze Tutorial negativ gegenüber C# gehalten ist, aber diverse unnötige Seitenhiebe sind schon zu sehen.

 

Btw kleine Anmerkung: double ist nicht unbedingt langsamer als float

 

Oh und eine kleine Ergänzung zum Tutorial ansich, Properties!

 

private int currentValue = 0;
public int NextValue
{
 get  // man kann hier auch private, public, protected, etc davor schreiben
 {
   currentValue ++;
   return currentValue;
 }
 set // hier auch, um zB den setter nicht öffentlich zu machen
 {
   currentValue = value;
 }
}

Benutzung:

print(NextValue); // 1;
print(NextValue); // 2;
print(NextValue); // 3;
NextValue = 10;
print(NextValue); // 10;
print(NextValue); // 11;

 

Properties sind get und/oder (man braucht nicht beide definieren) set Methoden die wie normale Variablen verwendet werden können (mit kleinen Einschränkungen). Aus dem obrigen könnte man auch folgendes schreiben:

 

private int currentValue = 0;
public int getNextValue()
{
   currentValue ++;
   return currentValue;
}

public void setNextValue()
{
 currentValue = value;
}

Benutzung:

print(getNextValue()); // 1;
print(getNextValue()); // 2;
print(getNextValue()); // 3;
setNextValue(10);
print(getNextValue()); // 10;
print(getNextValue()); // 11;

Link zu diesem Kommentar
Auf anderen Seiten teilen

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung jetzt entfernen

  Only 75 emoji are allowed.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Lädt...
×
×
  • Neu erstellen...