Je me demande quelle est la bonne façon d'utiliser l'instance singleton: quand je crée une classe singleton appelée "Manager" et qu'elle contient une variable int appelée "value" et que j'ai une autre classe appelée "A" ... XXX
Donc, dans ma classe A, je peux créer une instance du singleton comme ceci: // exemple de pseudo code classe publique A {
// First
// using as a global variable
Manager manager;
//------------------------------------
Public void tstOne(){
// First
// using that global variable
manager.Instance.value = 0;
// Second
// or this way
Manager.Instance.value = 0;
}
Public void tstTwo(){
// First
// using that global variabl
manager.Instance.value = 1;
// Second
// or this way
Manager.Instance.value = 1;
}
}
Mon problème est donc: devrais-je créer une instance globale puis utiliser cette instance comme dans la première ou devrais-je utiliser le deuxième exemple?
Sont-ils tous les deux identiques en termes de consommation de mémoire et d'efficacité? Ou y a-t-il une autre façon d'utiliser les singletons?
3 Réponses :
Instance est un champ statique donc vous devriez obtenir vos références au singleton avec Manager.Instance .
Manager.Instance est une instance accessible globalement car elle est publique statique. Il n'y a pas de variable globale en C #.
Accéder à un champ statique directement par le nom de la classe est certainement mieux que de créer une deuxième instance en tant que champ dans A, puis d'accéder à l'instance statique en l'utilisant.
ok, @Ruzihm dites-vous directement en utilisant Manager.Instance. est la bonne façon d'utiliser au lieu de déclarer une instance globale. Y aura-t-il des explications supplémentaires à votre réponse
@macnmunasinghe Manager.Instance est une instance globalement accessible car il s'agit de public static , donc je ne sais pas quoi penser de votre question. Il n'y a pas de variable globale en C #. Quoi qu'il en soit, accéder à un champ statique directement par le nom de la classe est certainement mieux que de créer une deuxième instance en tant que champ dans A , puis d'accéder à l'instance statique en l'utilisant.
ouais, cela rend cela plus compréhensible cela devrait être la réponse
Je confirme la réponse de Ruzihm (et je suis également d'accord avec le commentaire de derHugo, je préfère personnellement SO et je les référence si nécessaire. Beaucoup plus propre que l'approche directe de Singleton ou DI).
Pour être précis, accéder au gestionnaire du membre de l'instance .Instance.value est plus lent et nécessite également une allocation supplémentaire de mémoire sur le tas, ce qui nuit à la fois à l'utilisation de la mémoire et aux performances de vitesse. (Très petits problèmes de performances, mais quand même.)
Il y a aussi d'autres possibilités d'amélioration sur le singleton, surtout si vous n'avez pas besoin qu'il dérive de MonoBehaviour.
Vous pouvez:
Ce serait l'implémentation (en considérant que vous utilisez le Manager comme instance d'assistance et YourManagerMono si vous avez besoin d'une logique de monobéissance:
public class YourManagerMono : MonoBehaviour
{
}
public sealed class Manager
{
private static readonly Manager instance = new Manager();
//use a gameobject to relate it in the scene
private YourManagerMono monoManager;
public static Manager Instance => instance;
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Manager() {}
private Manager()
{
//find or create a manager
monoManager = GameObject.FindWithTag("Manager").GetComponent<YourManagerMono>();
if (monoManager == null)
{
monoManager = new GameObject().AddComponent<YourManagerMono>();
monoManager.gameObject.tag = "Manager";
Object.DontDestroyOnLoad(monoManager.gameObject);
}
}
}
Un excellent article sur l'implémentation de singleton pour C # par Jon Skeet (j'ai utilisé mise en œuvre 4).
MODIFIER:
Encore une fois, je suis d'accord avec derHugo (dans le nouveau commentaire sur cette réponse). Mon exemple est utilisé pour montrer une perspective intéressante et pour offrir autant de performances que possible, mais si vous avez juste besoin d'un Monobehaviour avec juste les points 1 et 3, vous pouvez continuer avec un implémentation générique de singleton de Unify Community (n'oubliez pas de définir la classe de fin comme scellée pour aider le compilateur).
p >
Pas grand-chose lié à la question, alors je l'ajoute en commentaire. Cette vidéo montre comment utiliser SO pour une mise en œuvre plus propre.
c'est bien!!!
diviser cela en une deuxième classe au lieu de faire directement la même chose dans le MonoBehaviour est une question de goût je suppose ... Je ne le ferais pas de cette façon. Aussi la partie "paresseuse" ... dans la plupart des cas, vous ne voulez pas de décalage de jeu pendant le jeu .. il est donc probablement préférable d'instancier / d'ajouter etc. le composant dès le début de l'application où un décalage est toujours acceptable .
Avoir 2 cours n'est pas strictement obligatoire (mais je le vois comme une nouvelle option intéressante). L'implémentation paresseuse fonctionnera très probablement au démarrage (car certains scripts le demanderont) donc ce n'est pas un gros problème. Mais j'aime particulièrement le safecheck qui crée le singleton s'il manque dans la scène. En général, si vous ne voulez que le comportement unique avec un contrôle de sécurité (mise en œuvre paresseuse de sécurité) et non la deuxième classe, je pense que la solution Unify fonctionne très bien.
Pour les Singletons que vous voulez exister dans une seule scène, j'utilise:
public class GlobalSingleton<T> : MonoBehaviour
where T : GlobalSingleton<T>
{
static T s_instance = null;
public static T Instance
{
get
{
if (s_instance == null)
{
GameObject prefab = Resources.Load(typeof(T).Name) as GameObject;
if (prefab == null || prefab.GetComponent<T>() == null)
{
Debug.LogError("Prefab for game manager " + typeof(T).Name + " is not found");
}
else
{
GameObject gameManagers = GameObject.Find("GameManagers");
if (gameManagers == null)
{
gameManagers = new GameObject("GameManagers");
DontDestroyOnLoad(gameManagers);
}
GameObject gameObject = Instantiate(prefab);
gameObject.transform.parent = gameManagers.transform;
s_instance = gameObject.GetComponent<T>();
s_instance.Init();
}
}
return s_instance;
}
}
protected virtual void Init()
{ }
}
Et puis j'hérite de votre classe, comme ceci:
public class MyManager : SceneSingleton<MyManager>
dépend ... actuellement aucun de vos codes n'a beaucoup de sens .... où est l'implémentation de
Manager?Double possible de Comment implémenter un singleton en C #?
Il n'y a pas de questions stupides macnmunasinghe, j'espère que nous pourrons vous aider à clarifier ce que vous demandez et à trouver votre chemin vers la réponse. Ce guide peut vous être utile stackoverflow.com/help/how-to-ask
En fait, les singletons sont un anti-pattern bien connu, pensez à utiliser l'injection de dépendances à la place. Veuillez consulter ce lien par exemple.
@SamuelVidal cela dépend beaucoup de vos besoins. Les singletons sont souvent un bon moyen de fournir un accès global à un composant de gestionnaire. À mon avis, l'injection de dépendances 1. "cache" le genre de dépendance de la même manière qu'un motif singleton et 2. elle-même fonctionne en fait comme un gros singleton pour fournir les références souhaitées ... Si vous voulez vraiment être propre que de ne pas avoir de singletons ni d'injection de dépendances, mais plutôt de gérer vos références d'instance "mieux" (quoi que cela signifie) en référençant des éléments dans l'inspecteur dès le début.
@derHugo je ne suis pas d'accord ;-)
@SamuelVidal lol vous pouvez;) C'est pourquoi je dis à mon avis .. Je sais qu'il y a beaucoup de discussions sur les singletons vs l'injection de dépendances .. surtout dans les cercles d'Unity ... peut-être que je n'aime tout simplement pas DI sans raison; )
salut @derHugo, Manger est la classe singleton
@macnmunasinghe veuillez ajouter un exemple minimal, complet et reproductible ... il est difficile de dire où et comment vous appelez votre code
J'ai édité la question, j'espère qu'elle sera plus claire à comprendre maintenant
votre setter doit être privé comme dans
public static Manager Instance {get; ensemble privé;}. De plus, leManager manager;n'a toujours aucun sens .... où / quand cette référence sera-t-elle définie? Vous feriez alors simplementmanager.value = 0;à la place puisque vous avez déjà la référence à l'instance. DoncManager.Instanceest la seule référencestatique publiquequi a du sens à utiliser. Sinon, pourquoi auriez-vous besoin du modèle de singleton?@derHugo L'éditeur Unity n'est qu'un gigantesque et complexe framework d'injection de dépendances :)