[C#] Lelijke code voorkomen

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 23:31

diondokter

Dum spiro, spero

Topicstarter
Hallo beste tweakers!

Ik ben weer na een paar maanden bezig met mijn game in Unity.
Daar doe ik nu een grote schoonmaakbeurt en heb al veel verwijderd.

Dit geeft mij de kans om een aantal systemen opnieuw uit te denken.
Nu ben ik bezig met een nieuw Entity systeem:

Entity -> ToolEntity -> HammerEntity
Entity -> BuildingEntity -> SmelteryEntity
enz...

Entity is abstract en heeft een abstracte property 'UIFriendlyName'. Dit is de naam voor de entity die geschikt is om aan de gebruiker te laten zien. Elke subclass moet dus die property implementeren en een waarde geven.

Nu wil ik een lijst in de UI maken met alle entities die je in de game dan kan spawnen.
Dus met reflection haal ik alle non-abstract types op die een subclass zijn van Entity.

Misschien zie je het probleem al. Ik wil nu dus de UIFriendlyName hebben op basis van de entity Type.
Daarvoor heb ik al wel code gevonden die werkt, maar die schrijft zelf wat IL code en maakt een null instance. Erg lelijk! http://stackoverflow.com/...without-creating-instance

Een nieuwe instance maken voor elke gevonden type zou kunnen, maar dat lijkt mij niet echt handig met de verouderde GC die Unity heeft.

Heeft één van jullie een idee wat ik kan doen in mijn situatie?
Ik weet dat het niet bestaat, en ook waarom, maar abstract static members zouden erg fijn zijn in deze situatie.

Bedankt voor het lezen,
Dion Dokter

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 27-09 13:03
Maak ergens een object waarbij alle FriendlyNames worden geregistreerd door je objecten.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 23:31

diondokter

Dum spiro, spero

Topicstarter
farlane schreef op donderdag 8 december 2016 @ 17:37:
Maak ergens een object waarbij alle FriendlyNames worden geregistreerd door je objecten.
Hoe zie je dat voor je?
Ik zou dan nog steeds voor elke Type eerst een instance moeten maken...?

Acties:
  • 0 Henk 'm!

  • Daos
  • Registratie: Oktober 2004
  • Niet online
Je kan ook gewoon een public static field gebruiken:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class ABC
{
    public static string friendlyName = "abc";
}

class DEF
{
    public static string friendlyName = "def";
}

class Program
{
    static void Main(string[] args)
    {
        Type[] types = { typeof(ABC), typeof(DEF) };

        foreach (var t in types)
        {
            Console.WriteLine(t.GetField("friendlyName").GetValue(null));
        }
    }
}


edit:
Met static properties kan het ook:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
class ABC
{
    public static string FriendlyName 
    {
        get 
        {
            return "abc";
        }
    } 
}


t.GetProperty("FriendlyName").GetValue(null, null)

[ Voor 21% gewijzigd door Daos op 08-12-2016 17:52 ]


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Daos schreef op donderdag 8 december 2016 @ 17:46:
Je kan ook gewoon een public static field gebruiken:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class ABC
{
    public static string friendlyName = "abc";
}

class DEF
{
    public static string friendlyName = "def";
}

class Program
{
    static void Main(string[] args)
    {
        Type[] types = { typeof(ABC), typeof(DEF) };

        foreach (var t in types)
        {
            Console.WriteLine(t.GetField("friendlyName").GetValue(null));
        }
    }
}


edit:
Met static properties kan het ook:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
class ABC
{
    public static string FriendlyName 
    {
        get 
        {
            return "abc";
        }
    } 
}


t.GetProperty("FriendlyName").GetValue(null, null)
Ooit van interfaces gehoord? :X

[ Voor 3% gewijzigd door RobIII op 08-12-2016 17:55 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Daos
  • Registratie: Oktober 2004
  • Niet online
Hij wil een string uitlezen die in een klasse staat zonder er eerst een object van aan te maken. Als je dat in C# zelf wilt doen, ontkom je volgens mij niet aan static.

Heb jij anders een beter idee?

[ Voor 13% gewijzigd door Daos op 08-12-2016 18:37 ]


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 06-10 13:59
Een property file met class names en hun 'friendly' names die je on-startup inleest. Heb je ook nog eens als voordeel dat je die simpel kan localiseren. Dat een class zijn eigen 'display' string aan boord heeft is behoorlijk ranzig IMHO.

https://niels.nu


Acties:
  • +2 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Hydra schreef op donderdag 8 december 2016 @ 18:35:
[...]


Een property file met class names en hun 'friendly' names die je on-startup inleest.
Of een custom attribute maken en die als 'annotation' op een class zetten. Vervolgens on-startup alle classes langsgaan (idealiter doe je dat op basis van een interface/common baseclass o.i.d. zodat je niet veel te veel onnodig werk verricht) en die attribute uitlezen.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface IGameEntity { }

public class Player : IGameEntity { }

[GameEntityName("TheMan")]
public abstract class Enemy : IGameEntity { }

[GameEntityName("Spooky")]
public class Ghost : Enemy { }


public class NonGameEntity { }


public class GameEntityNameAttribute : Attribute
{
    public string Name { get; set; }
    public GameEntityNameAttribute(string name)
    {
        this.Name = name;
    }
}


Vervolgens dictionary bouwen met Key = Type, Value = String (Naam):

C#:
1
2
3
4
5
6
7
8
9
10
var gametypes = game.GetType().Assembly.GetTypes(); // Get a starting point for all types

var gameAtt = typeof(GameEntityNameAttribute);
var entities = gametypes
    .Where(t => typeof(IGameEntity).IsAssignableFrom(t)     // Must implement IGameEntity
            && t.GetCustomAttributes(gameAtt, true).Any())  // Must have a GameEntityNameAttribute
    .ToDictionary(
        t => t, 
        t => ((GameEntityNameAttribute)t.GetCustomAttributes(gameAtt, true).First()).Name
    );


Enemy: "TheMan"
Ghost: "Spooky"


Ja, de LINQ query hierboven is wat 'bulky'; met wat extensionmethods o.i.d. kun je dit nog wel wat mooier en compacter maar vooral leesbaarder schrijven ;)

[ Voor 65% gewijzigd door RobIII op 08-12-2016 19:03 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • biomass
  • Registratie: Augustus 2004
  • Laatst online: 20:58
Of een compile-time Dictionary<Type,string>() en die gebruiken? Heb je alles bij elkaar..

Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 23:31

diondokter

Dum spiro, spero

Topicstarter
Die attributen zijn misschien wel het beste idee. Dat zou ik ook kunnen enforcen door bij de startup een loopje te maken en dan een exception te gooien wanneer een entity geen attribuut heeft.

Acties:
  • 0 Henk 'm!

  • biomass
  • Registratie: Augustus 2004
  • Laatst online: 20:58
Bij het gebruik van de attributen heb je de friendly names los gedefinieerd in al je classes.

Als je een tijdens de startup binnen een methode de dictionary gewoon aanmaakt zonder Reflection, heb je alle friendly names in 1 source file bij elkaar.

Een exception gooien zou je kunnen doen als er tijdens het testen van je code een type ontbreekt in de initializer van je Dictionary. In je release build heb je die test niet nodig volgens mij.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Dictionary<Type, string> friendlynames = new Dictionary<Type, string>()
{
      {typeof(IGameEntity), string.Empty},
      {typeof(Enemy), "TheMan" },
      {typeof(Ghost), "Spooky" }
};
 

foreach (Type t in Assembly.GetExecutingAssembly().GetTypes().
             Where(t => typeof(IGameEntity).IsAssignableFrom(t)))
            {
                    if (friendlynames.ContainsKey(t) == false)
                    {
                        Console.WriteLine("releasing game without a name for {0}?", t.Name);
                    }
            }
 

Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 23:31

diondokter

Dum spiro, spero

Topicstarter
Zelf zo'n dictionary maken werkt prima. En dat lijkt op wat ik hiervoor had (Voor de schoonmaakbeurt).
Maar dat loopt erg snel uit de hand wanneer je richting de 30-40+ types gaat. Het doel is dus om al die variabelen binnen de class te houden, en dan het liefst geënforced (zoals bij een abstract) zodat dat niet mis gaat en je moet zoeken in welk bestand het allemaal gedefinieerd staat.

Wat dat betreft zullen de attributes waarschijnlijk het beste werken. Het staat dan welliswaar niet in de class zelf, maar wel dicht erbij. Ook kan ik het zelf gaan enforcen (helaas zal de IDE me er niet aan herinneren dan).

Acties:
  • 0 Henk 'm!

  • W1LL3M
  • Registratie: Augustus 2001
  • Laatst online: 14:32

W1LL3M

⭐⭐⭐⭐⭐

Kun je je classes zelf niet gewoon friendly namen? En de rest oplossen met namespaces.

Entity -> Entity.Tool -> Entity.Hammer (of bv Entity.Tool.Hammer)
Entity -> Entity.Building -> Entity.Smeltery

Dat scheelt een hoop gekunstel. En anders zou ik idd voor de custom attributes gaan.

Acties:
  • 0 Henk 'm!

  • spleethoven
  • Registratie: Oktober 2010
  • Laatst online: 24-01-2024
Dan zit je nog met het probleem wanneer je entity naam een spatie moet bevatten

Dan zou je nog een vertaalslag moeten doen op de naam om deze juist te krijgen

Entity -> Entity.Tool -> Entity.Hammer_Of_Doom

Acties:
  • 0 Henk 'm!

  • W1LL3M
  • Registratie: Augustus 2001
  • Laatst online: 14:32

W1LL3M

⭐⭐⭐⭐⭐

Underscores door spaties vervangen is nog wel te doen natuurlijk. Maar inderdaad, het kan zo zijn dat dit niet altijd ideale naamgeving opleverd.

Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 17:27

Haan

dotnetter

diondokter schreef op donderdag 8 december 2016 @ 18:54:
Die attributen zijn misschien wel het beste idee. Dat zou ik ook kunnen enforcen door bij de startup een loopje te maken en dan een exception te gooien wanneer een entity geen attribuut heeft.
Idealiter zou je dat met unit tests afdekken ;)

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 23:31

diondokter

Dum spiro, spero

Topicstarter
Haan schreef op vrijdag 9 december 2016 @ 15:57:
[...]

Idealiter zou je dat met unit tests afdekken ;)
Dat is nog eigenlijk best een goed idee!
(Al is het in unity niet zo straigt forward als je zou denken...)
Die gaat om mijn to-do lijst :)

Maar als iemand gaat modden, dan is die test er natuurlijk niet, dus ik laat de on-startup test wel staan.

[ Voor 14% gewijzigd door diondokter op 09-12-2016 16:12 ]


Acties:
  • 0 Henk 'm!

  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 19-09 10:19
(even een klein dingetje, maar ik was even in de war doordat je entity systeem zei, dat is namelijk juist zonder hierarchie zie bijvoorbeeld Wikipedia: Entity–component–system wat jij maakt is meer een soort inheritance tree van objecten).

Bij een entity systeem hoef je niet met annotaties of "percee deze class/interface implementeren" te werken om een bepaalde eigenschap te hebben. Maar je zou in je factory waar een entity in het UI/Spawn systeem zetten.

Heel ander design (met andere voor en nadelen). En veels te kort uitgelegd. Voor een betere uitleg kun je bijvoorbeeld naar het eerste antwoord kijken op deze vraag die ik ooit eens op StackOverflow heb gevraagd: http://gamedev.stackexcha...here-to-update-components

~ Mijn prog blog!


Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 23:31

diondokter

Dum spiro, spero

Topicstarter
Unity gebruikt ook zo'n component system. Kan heel handig zijn, maar na drie jaar ermee gewerkt te hebben, zie ik er ook veel onhandigheden in. Alles is namelijk verspreid, data is overal en nergens en overzicht hebben is erg lastig.

Wat ik hier dus probeer is mijn eigen aanpak doorzetten: Alles zoveel mogelijk centraliseren.
Ik ga dus meer voor een hierarchisch entity model, net zoals in Minecraft.

(Daarbij, 'Entity' kan letterlijk alles zijn: something that has a real existence; thing.)

Het is natuurlijk wel super leuk om over dit soort systemen en structuren na te denken, dus dank je voor de link!

Acties:
  • 0 Henk 'm!

  • Caelorum
  • Registratie: April 2005
  • Laatst online: 20:22
diondokter schreef op dinsdag 13 december 2016 @ 13:15:
Unity gebruikt ook zo'n component system. Kan heel handig zijn, maar na drie jaar ermee gewerkt te hebben, zie ik er ook veel onhandigheden in. Alles is namelijk verspreid, data is overal en nergens en overzicht hebben is erg lastig.[...]
Het mooie van een componentsysteem is juist dat alle components van hetzelfde type bij elkaar zijn. Dus niet per entity gegroepeerd, maar per type component. Vaak manipuleer je ook alleen een bepaald type eigenschap van alle entities in 1 keer (bijv. updaten van animaties?). Door die componenten in 1 block achter elkaar in het geheugen te zetten kan je dit snel doen (of iig een stuk sneller dan als je random door het geheugen moet springen).
Er zijn uiteraard wel andere issues die een model zoals die jij hebt niet heeft.
Moet nog wel de kanttekening plaatsen dat een component model gebruiken niet ook kan betekenen dat je alsnog referenties vanuit je entiteit kan hebben. Alleen deze componenten worden niet gemaakt door de entiteit.

Acties:
  • 0 Henk 'm!

  • R4gnax
  • Registratie: Maart 2009
  • Laatst online: 06-09 17:51
Waarom je Entity objecten niet centraal registreren via een MEF catalog en die UIFriendlyName als metadata bij de export van je entities meegeven?

Ben je gelijk helemaal klaar tot aan je dependency injection toe en is alles mooi uitbreidbaar met automatische wire-up voor alle entity types.

[ Voor 27% gewijzigd door R4gnax op 14-12-2016 21:04 ]


Acties:
  • 0 Henk 'm!

  • sorted.bits
  • Registratie: Januari 2000
  • Laatst online: 02-10 21:21
Als het aan mij lag ging ik niet alle variabele namen een suffix geven van het type. Dit is binnen C# ook overbodig, code completion is fantastisch en je ziet direct wat voor type het is.

Daarna de ToString van de classes overriden zodat je een goede naam kan teruggeven.

Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 23:31

diondokter

Dum spiro, spero

Topicstarter
sorted.bits schreef op woensdag 14 december 2016 @ 21:07:
Als het aan mij lag ging ik niet alle variabele namen een suffix geven van het type. Dit is binnen C# ook overbodig, code completion is fantastisch en je ziet direct wat voor type het is.

Daarna de ToString van de classes overriden zodat je een goede naam kan teruggeven.
Klopt, dat zou wel mooi zijn. Maar ik werk in Unity dus ook buiten de IDE, met assets die vergelijkbare namen hebben. Daarvoor is het vaak fijner om erg expliciet te zijn.

Ook zou de ToString niet helpen bij het verkrijgen van de naam met alleen de Type.
Dank je voor de suggestie. Ik neem aan dat je er best mooie applicaties mee kan maken, maar het is denk ik te uitgebreid en abstract voor wat ik wil. Daarbij kan Unity er waarschijnlijk nog niet mee overweg omdat die een .NET versie van ~2 gebruikt.

Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 23:31

diondokter

Dum spiro, spero

Topicstarter
Niet het artikel zelf, maar wel het principe. Zo werkt Unity namelijk ook.
Mijn vorige systeem werkte ook zo, type gedefenieerd door componenten. Het heeft veel voordelen, maar ook veel nadelen.

Zo is al je code verspreid door het hele project, kun je heel snel fouten maken door een component teveel/ te weinig/verkeerd neer te zetten en is er bijna geen controle op die fouten.

Maar als je wat kleiners maakt of alles in de Unity 'stijl' doet, dan zorgt het voor een hele snelle workflow.

Maar ik gebruik lang niet alle capaciteiten van Unity en schat zelf in dat ik efficienter kan zijn door gedeeltelijk 'deep hierarchies' te gebruiken.
Unity doet veel goed en veel fout en ik denk dat ik nu dicht in de buurt zit van het goede en ver weg ben van het slechte. (Best of both worlds)

(Even ter verduidelijking: Alleen de entities zijn hierarchisch, de rest is met components)

Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

diondokter schreef op woensdag 14 december 2016 @ 22:11:
Niet het artikel zelf, maar wel het principe. Zo werkt Unity namelijk ook.
Mijn vorige systeem werkte ook zo, type gedefenieerd door componenten. Het heeft veel voordelen, maar ook veel nadelen.

Zo is al je code verspreid door het hele project, kun je heel snel fouten maken door een component teveel/ te weinig/verkeerd neer te zetten en is er bijna geen controle op die fouten.
Je bedoelt dat je geen testen schrijft? O-)

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • Stukfruit
  • Registratie: Oktober 2007
  • Niet online
diondokter schreef op woensdag 14 december 2016 @ 21:45:
Dank je voor de suggestie. Ik neem aan dat je er best mooie applicaties mee kan maken, maar het is denk ik te uitgebreid en abstract voor wat ik wil. Daarbij kan Unity er waarschijnlijk nog niet mee overweg omdat die een .NET versie van ~2 gebruikt.
Als je nog een beetje geduld hebt: ze zijn bezig om ondersteuning voor 4.6 toe te voegen:
https://forum.unity3d.com...editor-on-5-5-0b9.438359/

Daarna kunnen de Unity'ers na vele jaren klagen eindelijk C# 6-features, een moderne GC en de nieuwere features van het .NET-framework (in dit geval natuurlijk via Mono) gebruiken :P
diondokter schreef op dinsdag 13 december 2016 @ 13:15:
Unity gebruikt ook zo'n component system. Kan heel handig zijn, maar na drie jaar ermee gewerkt te hebben, zie ik er ook veel onhandigheden in. Alles is namelijk verspreid, data is overal en nergens en overzicht hebben is erg lastig.
Die drab van Unity is inderdaad erg onhandig. Daarom zijn er mensen die andere oplossingen hebben bedacht, zoals bv. Zenject, StrangeIoC en nog een paar van dat soort frameworks. Dat is in het begin even doorzetten omdat het heel anders is, maar je hebt daarna wel de mogelijkheid om alles een stuk netter en modulair te houden.

In het geval van StrangeIoC kan ik je trouwens aanraden om gelijk signals te gebruiken.

Dat zit wel Schnorr.


Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 23:31

diondokter

Dum spiro, spero

Topicstarter
H!GHGuY schreef op woensdag 14 december 2016 @ 22:18:
[...]
Je bedoelt dat je geen testen schrijft? O-)
Is het vaststellen hoe een object er uit ziet niet juist waar het component model van weg wil?
Want dat is wat je tests doen als je daarop wil testen... 8)7

Ik schrijf nu wel tests, omdat C# geen geënforcede attributes heeft...
Stukfruit schreef op woensdag 14 december 2016 @ 22:20:
Als je nog een beetje geduld hebt: ze zijn bezig om ondersteuning voor 4.6 toe te voegen:
https://forum.unity3d.com...editor-on-5-5-0b9.438359/
Ja super, wordt ook wel tijd na 10 jaar :P
Stukfruit schreef op woensdag 14 december 2016 @ 22:20:
Die drab van Unity is inderdaad erg onhandig. Daarom zijn er mensen die andere oplossingen hebben bedacht, zoals bv. Zenject, StrangeIoC en nog een paar van dat soort frameworks. Dat is in het begin even doorzetten omdat het heel anders is, maar je hebt daarna wel de mogelijkheid om alles een stuk netter en modulair te houden.
Ben gelukkig niet de enige ;)
Voor dit project ben ik al te ver om over te stappen op zo'n framework, maar het is zeker iets om naar te kijken wanneer ik een nieuw project start.

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

diondokter schreef op woensdag 14 december 2016 @ 23:11:
Is het vaststellen hoe een object er uit ziet niet juist waar het component model van weg wil?
Want dat is wat je tests doen als je daarop wil testen... 8)7
Nee, je testen zouden het gewenste gedrag moeten testen, gedrag dat door je componenten geleverd wordt. Dit soort zaken zou je wsch met integratietesten moeten kunnen vinden.

ASSUME makes an ASS out of U and ME

Pagina: 1