Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!
Werbeanzeige
C#-Quelltext
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 using System; using System.Collections.Generic; namespace Alice { /// <summary> /// Die ResourceManager Klasse verwaltet TResource Ressourcen wie Texture2D oder SoundEffect /// in einem Dictionary. Mittels Referenzzählung sollen doppelt erzeugte Ressourcen und /// deren Freigabe gesteuert werden. TResource muss zwingend IDispose implementieren, weil /// ResourceManager keinen Finalizer, um TResource frei zu geben, aufruft. /// Für das Dispose Pattern habe ich folgende Quelle verwendet: /// https://msdn.microsoft.com/de-de/library/fs2xkftw(v=vs.110).aspx /// </summary> /// <typeparam name="TResource">Der zu verwaltende Typ.</typeparam> public sealed class ResourceManager<TResource> : IDisposable where TResource : IDisposable { /// <summary> /// ManagedResource ist ein Container der die Ressource hält und deren Referenzzählung /// übernimmt. Instanzen von ihr werden in der Get-Methode von ResourceManager erzeugt. /// </summary> public sealed class ManagedResource : IDisposable { private int m_referenceCounter = 0; private ResourceManager<TResource> m_resourceManager = null; public string Key { get; private set; } public TResource Resource { get; private set; } private bool m_disposed = false; /// <summary> /// ManagedResource braucht die ResourceManager-Instanz um sich aus der Liste /// des Managers auszutragen (in ReleaseReference) und seine eigene freigabe anzustoßen. /// </summary> /// <param name="resourceManager">Der ResourceManager der diese Instanz verwaltet.</param> /// <param name="key">Der Dateipfad zur Ressource.</param> /// <param name="resource"></param> public ManagedResource(ResourceManager<TResource> resourceManager, string key, TResource resource) { m_resourceManager = resourceManager; Key = key; Resource = resource; } public void AddReference() { ++m_referenceCounter; } public void ReleaseReference() { if (m_referenceCounter <= 0) return; --m_referenceCounter; if (m_referenceCounter <= 0) { m_resourceManager.Destroy(Key); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (m_disposed) return; if (disposing) { Resource.Dispose(); } m_disposed = true; } } private Dictionary<string, ManagedResource> m_managedResourceList = new Dictionary<string, ManagedResource>(); private bool m_disposed = false; public string RootDirectory { get; private set; } /// <summary> /// Dieses Delegate wird für das eigentliche laden aus einer Datei verwendet. /// </summary> /// <param name="key">Key ist der Dateipfad.</param> /// <returns>Gibt die angeforderte Ressource zurück.</returns> public delegate TResource ResourceLoader(string key); private ResourceLoader m_resourceLoader = null; public ResourceManager(ResourceLoader resourceLoader, string rootDirectory) { m_resourceLoader = resourceLoader; RootDirectory = rootDirectory; } /// <summary> /// Die Get-Methode ist die Ressourcen anfrage. /// Erst wird nachgeschaut ob der Schlüssel (Dateipfad) vorhanden ist. /// Falls ja, wird gleich die Ressource rausgeholt. /// Falls nein, wird /// 1. Die Ressource geladen /// 2. Eine ManagedRessource angelegt und ihr der Manager, der Schlüssel und die /// zu verwaltende Ressource übergeben /// 3. Die ManagedResource wird in die Liste eingetragen /// Danach wird der Referenzzähler in managedResource um eins erhöht und managedResource /// zurück gegeben. /// </summary> /// <param name="key"></param> /// <returns></returns> public ManagedResource Get(string key) { bool containsKey = m_managedResourceList.TryGetValue(key, out ManagedResource managedResource); if(!containsKey) { TResource resource = m_resourceLoader(System.IO.Path.Combine(RootDirectory, key)); managedResource = new ManagedResource(this, key, resource); m_managedResourceList.Add(key, managedResource); } managedResource.AddReference(); return managedResource; } /// <summary> /// Destroy gibt die angegebene Ressource frei, ruft Dispose auf und entfernt die /// Ressource aus der liste. /// </summary> /// <param name="key"></param> private void Destroy(string key) { bool containsKey = m_managedResourceList.TryGetValue(key, out ManagedResource managedResource); if (!containsKey) return; managedResource.Dispose(); m_managedResourceList.Remove(key); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (m_disposed) return; if(disposing) { foreach (ManagedResource managedResource in m_managedResourceList.Values) { managedResource.Dispose(); } m_managedResourceList.Clear(); } m_disposed = true; } } }
C#-Quelltext
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 using System.IO; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace Alice.Graphic { public class TextureLoader { private GraphicsDevice m_graphicsDevice = null; private Texture2D m_nullTexture = null; private const int NULL_TEXTURE_SIZE = 64; public TextureLoader(GraphicsDevice graphicsDevice) { m_graphicsDevice = graphicsDevice; CreateNullTexture(); } private void CreateNullTexture() { Color[] colorData = new Color[NULL_TEXTURE_SIZE * NULL_TEXTURE_SIZE]; Color green = new Color(0.0f, 1.0f, 0.0f, 1.0f); for(int i=0; i<colorData.Length; ++i) { colorData[i] = green; } m_nullTexture = new Texture2D(m_graphicsDevice, NULL_TEXTURE_SIZE, NULL_TEXTURE_SIZE); m_nullTexture.SetData(colorData); } public Texture2D Load(string filename) { Texture2D texture; try { using (var stream = File.OpenRead(filename)) { texture = Texture2D.FromStream(m_graphicsDevice, stream); } } catch { texture = m_nullTexture; } return texture; } } }
C#-Quelltext
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 using System; using System.Collections.Generic; namespace Alice { /// <summary> /// Die ResourceManager Klasse verwaltet TResource Ressourcen wie Texture2D oder SoundEffect /// in einem Dictionary. Mittels Referenzzählung sollen doppelt erzeugte Ressourcen und /// deren Freigabe gesteuert werden. TResource muss zwingend IDispose implementieren, weil /// ResourceManager keinen Finalizer, um TResource frei zu geben, aufruft. /// Für das Dispose Pattern habe ich folgende Quelle verwendet: /// https://msdn.microsoft.com/de-de/library/fs2xkftw(v=vs.110).aspx /// </summary> /// <typeparam name="TResource">Der zu verwaltende Typ.</typeparam> public sealed class ResourceManager<TResource> : IDisposable where TResource : IDisposable { /// <summary> /// Das dazwischen geschobene Interface. /// Die anderen kommentare hab ich jetzt nicht angepasst. /// </summary> public interface IManagedResource { string Key { get; } TResource Resource { get; } void AddReference(); void ReleaseReference(); } /// <summary> /// ManagedResource ist ein Container der die Ressource hält und deren Referenzzählung /// übernimmt. Instanzen von ihr werden in der Get-Methode von ResourceManager erzeugt. /// </summary> private sealed class ManagedResource : IManagedResource, IDisposable { private int m_referenceCounter = 0; private ResourceManager<TResource> m_resourceManager = null; public string Key { get; private set; } public TResource Resource { get; private set; } private bool m_disposed = false; /// <summary> /// ManagedResource braucht die ResourceManager-Instanz um sich aus der Liste /// des Managers auszutragen (in ReleaseReference) und seine eigene freigabe anzustoßen. /// </summary> /// <param name="resourceManager">Der ResourceManager der diese Instanz verwaltet.</param> /// <param name="key">Der Dateipfad zur Ressource.</param> /// <param name="resource"></param> public ManagedResource(ResourceManager<TResource> resourceManager, string key, TResource resource) { m_resourceManager = resourceManager; Key = key; Resource = resource; } public void AddReference() { ++m_referenceCounter; } public void ReleaseReference() { if (m_referenceCounter <= 0) return; --m_referenceCounter; if (m_referenceCounter <= 0) { m_resourceManager.Destroy(Key); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (m_disposed) return; if (disposing) { Resource.Dispose(); } m_disposed = true; } } private Dictionary<string, ManagedResource> m_managedResourceList = new Dictionary<string, ManagedResource>(); private bool m_disposed = false; public string RootDirectory { get; private set; } /// <summary> /// Dieses Delegate wird für das eigentliche laden aus einer Datei verwendet. /// </summary> /// <param name="key">Key ist der Dateipfad.</param> /// <returns>Gibt die angeforderte Ressource zurück.</returns> public delegate TResource ResourceLoader(string key); private ResourceLoader m_resourceLoader = null; public ResourceManager(ResourceLoader resourceLoader, string rootDirectory) { m_resourceLoader = resourceLoader; RootDirectory = rootDirectory; } /// <summary> /// Die Get-Methode ist die Ressourcen anfrage. /// Erst wird nachgeschaut ob der Schlüssel (Dateipfad) vorhanden ist. /// Falls ja, wird gleich die Ressource rausgeholt. /// Falls nein, wird /// 1. Die Ressource geladen /// 2. Eine ManagedRessource angelegt und ihr der Manager, der Schlüssel und die /// zu verwaltende Ressource übergeben /// 3. Die ManagedResource wird in die Liste eingetragen /// Danach wird der Referenzzähler in managedResource um eins erhöht und managedResource /// zurück gegeben. /// </summary> /// <param name="key"></param> /// <returns></returns> public IManagedResource Get(string key) { bool containsKey = m_managedResourceList.TryGetValue(key, out ManagedResource managedResource); if(!containsKey) { TResource resource = m_resourceLoader(System.IO.Path.Combine(RootDirectory, key)); managedResource = new ManagedResource(this, key, resource); m_managedResourceList.Add(key, managedResource); } managedResource.AddReference(); return managedResource; } /// <summary> /// Destroy gibt die angegebene Ressource frei, ruft Dispose auf und entfernt die /// Ressource aus der liste. /// </summary> /// <param name="key"></param> private void Destroy(string key) { bool containsKey = m_managedResourceList.TryGetValue(key, out ManagedResource managedResource); if (!containsKey) return; managedResource.Dispose(); m_managedResourceList.Remove(key); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (m_disposed) return; if(disposing) { foreach (ManagedResource managedResource in m_managedResourceList.Values) { managedResource.Dispose(); } m_managedResourceList.Clear(); } m_disposed = true; } } }
Community-Fossil
Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer
Dieser Beitrag wurde bereits 10 mal editiert, zuletzt von »BlueCobold« (02.06.2017, 09:28)
Meine Resource-Manager funktionieren z.B. so:
1) Sie haben einen Cache. Wird etwas von außen angefragt und ist nicht im Cache, wird es (unter Umständen, siehe unten) geladen und zurückgegeben.
2) Sie sind nested und scoped. Heißt ich habe einen primären Resource-Manager (oder mehrere) und für Level und Menüs habe ich ihm (oder ihnen) unterstellte Child-Manager. Fragt nun ein Level oder ein Menü etwas an, wird es vom Child-Manager zurückgegeben und gehalten.
3) Hält ein Child-Manager eine Ressource und dieselbe wird im Parent-Manager ebenfalls angefragt, wandert sie einen Scope nach oben.
4) Fragt man ein Child-Manager nach einer nicht-gecachten Ressource, fragt er andere Manager aus demselben Scope, ob sie die Resource haben und gibt dann deren zurück.
5) Fragt man ein Child-Manager nach einer nicht-gecachten Ressource, fragt er den Parent-Manager, ob er die Resource hat und gibt dann dessen zurück.
6) Man kann keine Resource explizit aus einem Manager entfernen, denn das geht niemanden etwas an
7) Der Manager gibt immer nur weak/unowned-References zurück. Wenn er eine Ressource also freigibt, ist sie weg. Niemand anders kann somit Ressourcen halten und an der Freigabe hindern oder sie freigeben, obwohl sie gebraucht wird.
Community-Fossil
Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer
Ich habe damit nirgendwo gesagt oder sagen wollen, dass der Umfang seines Managers dem meiner entsprechen muss (ist traurig, das ich das jetzt explizit nochmal verdeutlichen muss).
Werbeanzeige