Jump to content
Unity Insider Forum
  • Announcements

    • Lars

      Allgemeine Forenregeln   03/13/2017

      Forenregeln Nimm dir bitte einen Moment um die nachfolgenden Regeln durchzulesen. Wenn du diese Regeln akzeptierst und die Registration fortsetzen willst, klick einfach auf den "Mit der Registrierung fortfahren"-Button. Um diese Registration abzubrechen, klick bitte einfach auf den "Zurück" Button deines Browsers. Wir garantieren nicht für die Richtigkeit, Vollständigkeit und Brauchbarkeit der Nachrichten und sind auch nicht dafür verantwortlich. Die Beiträge drücken die Meinung des Autors des Beitrags aus, nicht zwangsläufig das, wofür die Forensoftware steht. Jeder Nutzer, der denkt, dass ein veröffentlichter Beitrag unzulässig bzw. störend ist, ist aufgefordert uns unverzüglich per E-Mail zu kontaktieren. Wir haben das Recht störende Beiträge zu löschen und bemühen uns, das in einem realistischem Zeitraum zu erledigen (sofern wir beschlossen haben, dass die Löschung notwendig ist). Du akzeptierst, durchgehend während der Nutzung dieses Services, dass du dieses Forum nicht dazu missbrauchen wirst, Inhalte zu veröffentlichen, welche bewusst falsch und/oder verleumderisch, ungenau, beleidigend, vulgär, hasserfüllt, belästigend, obszön, sexuell belästigend, bedrohlich, die Privatsphäre einer Person verletzend oder in irgend einer Art und Weise das Gesetz verletzen. Des Weiteren akzeptierst du, dass du keine urheberrechtlich geschützte Inhalte ohne Erlaubnis des Besitzers in diesem Forum veröffentlichst. Mit dem Klick auf den "Mit der Registrierung fortfahren"-Button, akzeptierst du zudem unsere Datenschutzerklärung und stimmst der Speicherung deiner IP-Adresse und personenbezogenen Daten zu, die dafür benötigt werden, um dich im Falle einer rechtswidrigen Tat zurückverfolgen zu können bzw. permanent oder temporär aus dem Forum ausschließen zu können. Es besteht keine Pflicht zur Abgabe der Einwilligung, dies erfolgt alles auf freiwilliger Basis.   Zusatzinformationen Der Forenbetreiber hat das Recht, Nutzer ohne Angabe von Gründen permanent aus dem Forum auszuschließen. Des Weiteren hat er das Recht, Beiträge, Dateianhänge, Umfrage, Blogeinträge, Galleriebilder oder Signaturen ohne Angabe von Gründen zu entfernen. Mit der Registrierung verzichtest du auf alle Rechte an den von dir erstellten Inhalten, bzw. treten diese an das Unity-Insider.de und Unity-Community.de ab. Dies bedeutet im Klartext, dass das Unity-Insider.de und Unity-Community.de frei über deine Texte verfügen kann, sofern diese nicht wiederum die Rechte anderer verletzen. Es besteht weiterhin kein Anspruch von registrierten Nutzern bzw. ehemaligen registrierten Nutzern darauf, dass erstellte Inhalte und/oder die Mitgliedschaft (User) wieder gelöscht werden (Erhaltung der Konsistenz dieses Forums).   Einwilligungserklärung Wenn du mit der Speicherung deiner personenbezogenen Daten sowie den vorstehenden Regeln und Bestimmungen einverstanden bist, kannst du mit einem Klick auf den Mit der Registrierung fortfahren-Button unten fortfahren. Ansonsten drücke bitte Zurück. Stand: 07.03.2011
Sign in to follow this  
John

C# - Be smart [Tipp and Tricks]

Recommended Posts

Heyo :lol:,

ich wollte hier ein Thread erstellen in dem es darum geht kleine aber feine Tricks zu Listen die man jeweils beim Programmieren oftmals nicht im Kopf hat und benutzen kann um smarten Code zu schreiben :D

Index

Basics

 

#1. Der If-Else Terminator

Source-Code:

Before:

//...
[SerializeField]
private GameObject _obj1;
[SerializeField]
private GameObject _obj2;
[SerializeField]
private GameObject _obj3;
[SerializeField]
private GameObject _obj4;

//...
if (_obj1.activeSelf)
{
	_obj1.SetActive(false);
}
if (_obj2.activeSelf)
{
	_obj2.SetActive(false);
}
if (_obj3.activeSelf)
{
	_obj3.SetActive(false);
}
if (_obj4.activeSelf)
{
	_obj4.SetActive(false);
}
//...

After:

//...
[SerializeField]
private GameObject _obj1;
[SerializeField]
private GameObject _obj2;
[SerializeField]
private GameObject _obj3;
[SerializeField]
private GameObject _obj4;

//...
var objs = new GameObject[] {_obj1, _obj2, _obj3, _obj4};
foreach (var obj in objs)
{
	if (obj.activeSelf)
	{
		obj.SetActive(false);
	}
}
//...

Detail-Erklärung:

Oftmals besitzt man eine feste Anzahl an Variablen die man gerne ändern möchte. Nicht immer sind die in einem Array und darum passiert es schnell das man eine endlose "if-else"-Abfrage hat in dem man jeweils die Variable überprüft und dan eine action ausführt. Da können sich schnell Fehler einschleichen oder das ändern bzw. hinzufügen neue Sachen ist oftmals sehr mühselig.

Tipp:

Erstellt einen temporären Array in dem ihr alle Objects zusammenfasst und lässt anschließend ein For-/-each-Loop rüber laufen und tada ihr hab alles zusammengefasst und müsst nur noch in diesem Abschnitt was ab ändern und es wird sofort auf alle Objects übernommen. So seid ihr auf der sicheren Seite was Änderungen angeht.

Mfg.

-John

Share this post


Link to post
Share on other sites

Schönes Beispiel für eine solche Nutzung, wobei man im Beispiel eigentlich auch gleich schreiben kann:

//...
[SerializeField]
private GameObject[] _allObjects = new GameObject[3];
//...

foreach (GameObject obj in _allObjects)
{
   if (obj.activeSelf) obj.SetActive(false);
}

Das geht natürlich nur bedingt, wenn du eine externe Klasse einbinden musst, die Variablen bereits einzeln übergibt. Aber wenn Variablen eh keine Aussagekraft besitzen (z.b. "Image1", "Image2" etc), dann würde ich sie gleich in ein Array packen.

  • Like 1

Share this post


Link to post
Share on other sites

Gekürzt

//...
using System.Linq;

//...
[SerializeField] private GameObject[] gameObjects;


//...
gameObjects.ToList().ForEach(go => { if (go.activeSelf) go.SetActive(false); });
//...

 

Share this post


Link to post
Share on other sites

@Zer0Cool & LifeIsGood ihr hab völlig recht in fall das man von dem gleichen Object eine Collection besitzt sollte man auf ein Array zurückgreifen und das jeweils mit eine SerializeField versehen wird. Aber ich gibt auch fälle wo man keine Collection jeweils davon besitzen möchte :D Aber der beste Hinweis ist wie @Zer0Cool gesagt hat "item01, item02, item3..." wen das auftaucht sollte man darüber nachdenken eine Collection zu machen.

 

Ich habe nochmal ein Beispiel erstellt um es besser zu erklären ;)

//...
[SerializeField]
private int _goldAmount;
[SerializeField]
private int _diamontAmount;
[SerializeField]
private int _rubyAmount;
[SerializeField]
private int _perlAmount;

//...
var amounts = new int[] {_goldAmount, _diamontAmount, _rubyAmount, _perlAmount};
foreach (var amount in amounts)
{
	if(amount < 0)
	{
		signal.Publish<NegativAmountEvent>();
	}
}
//...

In der Klasse selbst hat man jeweils noch andere Provider oder Behaviours die direkt mit den Variable z.b. individuell arbeiten.

Share this post


Link to post
Share on other sites

#2. Extract If-Condition in Methods

Before:

//...
private void OnRequestJoinSession()
{
	if (_playerData.HasEquipKey
		&& _playerData.EquipKeyId == _sessionData.RequiredKeyId
		&& _playerData.Level > _sessionData.RequiredPlayerLevel)
	{
		//JoinSession...
	}
}
//...

After:

//...
private void OnRequestJoinSession()
{
	if (IsPlayerValid())
	{
		//JoinSession...
	}
}

private bool IsPlayerValid()
{
	return _playerData.HasEquipKey
		&& _playerData.EquipKeyId == _sessionData.RequiredKeyId
		&& _playerData.Level > _sessionData.RequiredPlayerLevel;
}
//...

Detail-Erklärung:

Ich habe schon oft erlebt das man meistens eine If-Condition hat die ein wenig mehr Logik erfordert. Oftmals schleichen sich hier schnell Fehler ein oder das lesen dieser Condition ist meistens mühselig. Für diese Fälle ist es praktisch diese in eine separate Methode zu verlagern um einerseits den Lesefluss zu erleichtern und auf der anderen Seite Fehler vorzubeugen. Falls man in seiner Klasse zum Beispiel öfters diese Condition überprüfen muss lohnt es sich auf jedenfalls diese in eine separate Methode zu Verlagen.

Tipp:

Ich benutze die Faustregel oft falls ich eine Condition habe die mehr als ein State überprüft verlagere ich diese in eine andere Methode. Oftmals macht es auch Sinn selbst bei eine Condition diese in einer separaten Methode zu verlagern z.B. bei größeren Überprüfungen. Es erleichtert das ab ändern später falls sich die Condition ändert und verbessert den Lesefluss des source-codes

Zusatz-Beispiel:

//...
private bool IsNumberNotZero()
{
	return int.Parse(_uiText.text) != 0;
}
//...

 

  • Like 1

Share this post


Link to post
Share on other sites

Find ich gut, ein andere Methode wäre sehr konsequent auf die Bezeichner zu achten und den Klassen der Objekte selbst Methoden für die Prüfung an die Hand zu geben:

private void OnRequestJoinSession()
{
	if (m_currentPlayerData.HasEquipKey() &&
	    m_currentPlayerData.EquipKeyIdEquals(m_CurrentSessionData.RequiredKeyId) &&
	    m_currentSessionData.CheckRequiredPlayerLevel(m_currentPlayerData.Level))
	{
		//JoinSession...
	}
}

 

  • Like 1

Share this post


Link to post
Share on other sites

@Zer0Cool Du hast völlig recht das wäre der Ideal Fall. Die jeweilige evaluierung in die Klassen (bzw. Model ) selbst zu verfrachten :)  Super punkt nochmal den du mit dem Example hervorgebracht hast.

Share this post


Link to post
Share on other sites

#3. Simple Namings: Keine Type Beschreibung oder Falsch Information

Before:

//...
private GameObject[] _unitList;
//...
private List<int> _scores;
//...
private GameObject _player01;
//...
private GameObject _player02;
//...

After:

//...
private GameObject[] _units;
//...
private List<int> _lastScoreCollection;
//...
private PlayerContainer _playerContainer;
//...

Detail-Erklärung:

Ein richtiger Variablen-Name zu finden kann oftmals eine riesige Hürde sein und dann passiert es sehr schnell das man seinen Variablen-Name verkompliziert. Doch wie kann man dagegen angehen und welchen Trick gibt es für eine schnellere Findung von Variablen-Namen. 

Oftmals hilft es sogar wie im Falle von den units statt "-List" hinschreibt könnte man das ganze einfach sauber gestellten in dem man den Namen unit in die Mehrzahl verwandet units damit hat man schon Information geben das es sich hier nicht um eine einzelne Unit, sondern um eine Ansammlung von Units handelt.

In dem Falle von scores ist es zwar jetzt nicht aussagekräftig nur ein "-s" dran zu hängen. Hier kann man auch auf Hilf Wörter zurückgreifen die das ganze mehr unterstreichen. Wie zum Beispiel "-Collection" das gibt darüber Auskunft das die letzte Score Zusammen Collected werden und man mit Hilfe dieser die letzte Score sich holen kann.

Wichtiger Hinweis: Sicherlich wird sich jetzt der ein oder andere fragen: Was jetzt ein Unterschied macht wen ich „List“ hinschreibe anstatt von einer Collection?

Es könnte schnell dazu führen das andere Leser oder man selbst verwirrt ist. Falls man jeweils mit dieser variable arbeitet und diese aus besonderen Gründen von einer List<int> in ein Array von int’s ändern müsste könnte der Leser erwarten, dass er eine Liste bekommt. Darum ist es ideal wen man einen Namen nimmt der jetzt nicht auf einen Typen zurückschließt lässt.

Container ist ebenfalls ein oftmals Benutzer Name den einen öfters über den Weg läuft. Falls man zum Beispiel mehrere Spieler besitzt und diese gerne zusammenfassen will kann man dafür sogar eine extra Klasse anfertigen die diese hält und verwaltet. Ich benutze gerne den Namen Container wen ich in dieser Klasse eine Ansammlung von verschieden Typen habe.

Bei einer Collection erwarte ich jeweils eine Ansammlung exakt nur vom einem Typen und nichts Anderes. Im Gegenteil zu einem Container wo ich alles reinpacken kann und diese dort auf eine bestimmte Zeit lagern kann.

Tipp:

Oftmals sollte man variablen nicht verkomplizieren nur, weil sie cool klingen oder glatt unnötige Information liefern wie z.B. den Typen den in den Namen reinschreiben das ist total überflüssig da fast jede der IDE bereits diesen Job übernehmen und dadurch nur mehr Verwirrung gestiftet werden kann.

 

Share this post


Link to post
Share on other sites

#4. Das Zwiebel-Prinzip / News-Paper

Before:

//...		
private void OnGUI()
{
	EditorGUILayout.HelpBox(_infoMessage, MessageType.Info);
		_displayAdvancedSettings = EditorGUILayout.Foldout(_displayAdvancedSettings, "Advance Settings:");
	if (_displayAdvancedSettings)
	{
		_customizeXValue = EditorGUILayout.IntField(_customizeXValue, "Important Value");
		_selectedColor = EditorGUILayout.ColorField("Selected Color", _selectedColor);
		_customizeYValue = EditorGUILayout.FloatField(_customizeYValue, "Important Value");
	}
	if (GUILayout.Button("Execute... Something"))
	{
		ExecuteCalculation();
	}
	GUILayout.Label("Process Time" + _processTime);
}
//...

After:

//...		
private void OnGUI()
{
	DrawHelperHeader();
	DrawSettingsActivator();
	if (_displayAdvancedSettings)
	{
		DrawAdvancedSettings();
	}
	DrawExecuteArea();
}

private void DrawHelperHeader()
{
	EditorGUILayout.HelpBox(_infoMessage, MessageType.Info);
}

private void DrawSettingsActivator()
{
	_displayAdvancedSettings = EditorGUILayout.Foldout(_displayAdvancedSettings, "Advance Settings:");
}

private void DrawAdvancedSettings()
{
	_customizeXValue = EditorGUILayout.IntField(_customizeXValue, "Important Value");
	_selectedColor = EditorGUILayout.ColorField("Selected Color", _selectedColor);
	_customizeYValue = EditorGUILayout.FloatField(_customizeYValue, "Important Value");
}

private void DrawExecuteArea()
{
	if (GUILayout.Button("Execute... Something"))
	{
		ExecuteCalculation();
	}
	GUILayout.Label("Process Time" + _processTime);
}
//...

Detail-Erklärung:

Der Aufbau einer Kasse und deren Struktur ist ein wichtiger Bestandteil der oftmals in Vergessenheit gerat. Eine geordnete und saubere Klasse ist das fundamental für einen guten Lesefluss und Verständnis für eine Klasse. Doch wie sieht eine saubere Struktur eigentlich aus? Ein seltsames Phänomen das mir immer über den Weg läuft ist das öfters in der "OnGUI", "Update", "Start"... MonoBehaviour-Klassen auftreten. Dort wird oftmals alles was man möchtet in diese Methode reingeschrieben. Hier ist es von enormen Vorteil dieses in unterschiedliche Methoden zu Verlagen, da falls man Änderungen vornehmen will sich nicht erst durch den gewaltigen GUI-Code durchkämpfen muss, sondern man schaut sich jeweils einmal die OnGUI-Klasse an wo jeweils das UI-Behaviour beschrieben wird und springt dann anschließend in die jeweilige Methode in der man eine Änderung vornehmen will. Man erspart sich Zeit und erleichtert das Lesen und das Verständnis von Methoden. Diese Concept kann man auch jeweils für seinen Klassen-Aufbau anwenden.

//...

public class ...
{
	//Public-Variablen
  	//...(static, const,...)
  	//Private-Variablen
  	//...(static, const,...)
  
  	//Constructor
	//...(init, setup)
  
	//Public-Methoden
  	//...(static, override, virtual,...)
  	//Private-Methoden
  	//...(static, override, virtual,...)
  
	//Destructor
	//...(cleanup, destory)
}
//...

Oftmals wird auch der Vergleich vom Zeitung-Lesen genommen. Das man jeweils seine Header hat darunter folgen die Header2 danach folgt Letzt endlich der Artikel-Content. So kann man schon vorab abklären was einem gefällt bzw. was man sucht und dann zu dem jeweiligen Artikel-Content schneller navigieren ohne von Anfang sich durch jeden Artikel Content zu kämpfen bis man zu seiner passenden Stelle trifft.

Tipp:

Ich verfolge stets das Ziel den Leser meiner Klasse Oberhalb meiner Klasse darüber Aufzuklären was in dieser Klasse passiert und unterhalb findet er jeweils die tatsächliche Implementierung.

//...
// Top
public bool IsObjectInRange(object obj)
{
  return _playerRange < GetPlayerDistanceFromTarget(obj);
}

// Bottom
private float GetPlayerDistanceFromTarget(object obj)
{
  return Vector3.Distance(_playerObj, obj);
}
//...

Eine weitere Faustregel die man verfolgen kann in Public-Methoden beschreibe ich nur jeweils das Ausgeführte Behaviour das ich tatsächlich in den Private-Methoden ausführe. So bekomme ich schneller einen Überblick und Verständnis der Klasse. Wen ich von außen in diese Klasse springe und falls mich wirklich die Implementierung interessiert spring ich jeweils dann ein schritt tiefer zu den Private-Methoden.

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

×