Laden...

Variablen über Namen ansprechen

Letzter Beitrag vor 27 Tagen 8 Posts 363 Views
Variablen über Namen ansprechen

Hallo,

wahrscheinlich lachen mich jetzt alle aus, aber ich bin kein Profi 😃

Also folgendes Problem ( in Unity3d, ist aber nebensächlich wo ).

Ich hab z.b. eine Klasse mit mehreren Variablen. Also z.b.

int pinelogs;
int oaklogs;
int sprucelogs;

... ( sind viele )

So nun will ich aus einem anderen Script abfrage wie viele ich hab, kenne aber nur den Namen des Holzes ( also z.b. string pinelogs )

z.b.

public int getAmount(string res )
{
 	int amount = 0;
 	switch( res )
 	{
 		case "pinelogs": amount = pinelogs; break;
 		case "oaklogs": amount = oaklogs; break;
 		case "sprucelogs": amount = sprucelogs; break;
 	}
	return amount;
}

Natürlich kann man jetzt mit einer Switchanweisung alle abfragen, das sind aber super viele Zeilen und werden auch laufend mehr.

Daher die Frage: Geht es auch anders? Also unter pearl ( lange her ) konnte ich die Variable auch mit irgendeinem Sonderzeichen vorgestellt ansprechen nur mittels dem string also z.b. ( return ?res; ). Gibts so etwas auch in c#?

Danke für die Hilfe 😃

Niemand lacht dich aus, wir haben alle Mal angefangen.

Pearl ist aber eine Skriptsprache, C# wird kompiliert, da muss zur Compiletime feststehen, auf welche Variable Du zugreifen willst.
Mit anderen Worten: Geht nicht - zumindest nicht einfach so.

Es gibt einen Weg: Reflection
Grob gesagt kannst Du damit die Metadaten von allem (Klassen, Methoden, Variablen, etc.) abrufen und damit dann das alles verwenden, also auch Variablen abrufen.

var field = typeof(MyClass).GetField(res, BindingFlags.NonPublic | BindingFlags.Instance);
var value = (int)field.GetValue(this);
field.SetValue(this, value);

Das funktioniert, wäre aber nicht der beste Weg, da langsam und fehleranfällig.
Beides kann man zwar umgehen/optimieren, aber der Code wird dadurch nicht einfacher und mit der Optimierung hättest Du nichts gewonnen.
Außerdem kann das nicht dynamisch (z.B. aus einer Konfiguration) erweitert werden und deine Beschreibung klingt so, als könnte das durchaus interessant sein.

Stattdessen solltest Du ein Dictionary bemühen:

private readonly Dictionary<string, int> _values = new()
{
    // Hardcoded Werte, wenn nötig
    ["a"] = 1,
    ["b"] = 2,
    ["c"] = 3,
};
public int getAmount(string res)
{
    if (_values.TryGetValue(res, out var value))
        return value;

    return 0;
}
public void setAmount(string res, int value)
{
    _values[res] = value;
}

Oder die vielen anderen Möglichkeiten vom Dictionary.

Das hätte dann den Charme, dass Du z.B. aus einer Datei alle Arten mit den Werten lesen und in das Dictionary schreiben könntest.

Du könntest es auch noch ausbauen und nicht nur einen Wert je Eintrag speichern, sondern eine Klasse erstellen, die dann mehrere Werte kapselt und von der Du dann je Name eine Instanz dem Dictionary hinzufügst. So kannst Du auch mehrere Werte verwalten, ohne zig Dictionarys zu brauchen.

(Code im Browser geschrieben)

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

Also folgendes Problem ( in Unity3d, ist aber nebensächlich wo ).

Leider nicht ganz. Unity verwendet zwar C#, aber hier muss man leider relativ oft Dinge anders machen oder mit Workarounds arbeiten. Auch ist die C# Version in Unity alt; aktuell C#9 und auch mit einem abgespeckten Feature-Set.

Während/Nach dem Drama rund um Unity, weswegen dann auch der CEO Riccitiello gehen musste, sind viele Unity Entwickler zu Godot gewechselt, das ebenfalls .NET unter der Haube anbietet und viel moderner ist.

Hihi ich dachte es mir schon, ich seh den Wald vor lauter Bäumen mal wieder nicht, dabei hab ich einige Möglichkeiten schon selber benutzt.

Das Problem mit Dictionaries ist das Unity diese zwar wunderbar verwenden kann, aber der Editor die Werte nicht anzeigt. Man muß also entweder alles über die Konsole ausgeben um es zu prüfen oder einfach abschätzen ob das was passiert stimmen kann...

Aber Unity3d kann Array`s anzeigen und tatsächlich verwende ich im gleichen Projekt für etwas anderes einfach 2 Arrays ( oder in einem Fall auch 3 ). Sprich ich hab z.b. ein Array mit Strings das angibt um was es sich handelt und 1-2 Arrays mit Floats in denen die Werte stehen.

Und um an den Wert zu kommen ist es dann ja auch einfach:

private string[] names = new string[]{"pinelogs", "oaklogs", "sprucelogs"};

private float[] amount = new float[names.Length];

// Werte werden z.b. geladen oder berechnet und eingetragen

public float getResource( string typ )
{
	float res = 0f;
	for ( int i = 0; i < names.Length; i++ )
	{
		if ( names[i] == typ )
			res = amount[i];
	}
	return res;
}

public void setResource( string typ, float amo )
{
	for ( int i = 0; i < names.Length; i++ )
	{
		if ( names[i] == typ )
			amount[i] = amo;
	}
}

Natürlich kann man sie so auch manipulieren... Der Vorteil von Arrays ist einfach das Unity3d diese im Editor anzeigen kann, ich kann also zur Laufzeit im Editor ( nicht mehr nach dem Release ) sehen: Ah der Wert hat sich geändert, als ich z.b. 10 pinelogs verwendet habe...

Aber danke für den Gedankenanstoss, den hab ich gebraucht 😃

Anstelle von zwei Arrays die via Index miteinander verknüpft sind, kannst du dir eine Klasse aufbauen z.B.

public class Item : MonoBehavior{
[SerializeField] private string _name;
[SerializeField] private int _amount;

public string GetName() => _name;
public int GetAmount() => _amount
}

Dann kannst du in deinem eigentlichen Skript Items in einem Array verwenden und die Werte dort sehen und ändern.

Dann sollten beide aber Eigenschaften sein:

public string Name => _name;
public int Amount => _amount;

Oder unterstützt Unity keine Eigenschaften?

Und ginge statt string nicht auch ein enum? Oder woher kommt bei getResource( string typ )der Parameter?

Felder haben auf alle Fälle eine Eigenheit bei Unity im Editor.

Unity unterstützt zwar Properties, allerdings mit Einschränkungen. Getter im Editor werden akzeptiert, allerdings keine Setter.