2017-08-06 5 views
0

Oh boy ai-je passé beaucoup de temps sur ce problème ...Comment utiliser l'objet dérivé dans la liste des baseclass pour l'extension générique

Ce que je suis en train de faire est de cloner un objet de type GameObject qui a également contient une liste de Component. Cloner GameObject types n'est pas un problème, mais il semble que cette liste est un problème car il est cloné mais pas son contenu; Le contenu n'est pas copié mais simplement stocké.

Autant que je voudrais cloner ces composants de la même manière que je l'ai fait avec les gameobjects, ce n'est pas possible à cause de mon système.
Les composants sont attachés à un GameObject et définissent fondamentalement le GameObject. Par exemple, si je voulais créer un moyen pour l'utiliser pour contrôler un GameObject je créerais un composant appelé quelque chose comme PlayerController qui proviendraient de type de base Component, en regardant quelque chose comme ceci:

class Component 
{ 
    //The gameobject this component is attached to 
    public GameObject gameObject { get; set;} 

    public virtual void Update() 
    { 
     //.. 
    } 
} 

class PlayerController : Component 
{ 

    override Update() 
    { 
     Move(); 
     //.. 
    } 

    public void Move() 
    { 
     //... 
    } 
} 

La façon dont je m clonage gameobject est dans une méthode comme ceci:

public GameObject Clone(Vector2 position, float scale) 
{ 
    GameObject source = this; 
    GameObject clone = new GameObject(); 
    //Get all properties of a GameObject 
    var srcProperties = TypeDescriptor.GetProperties(typeof(GameObject)).Cast<PropertyDescriptor>(); 
    //Assign all properties from the source to the clone 
    foreach (var srcProperty in srcProperties) 
    { 
     srcProperty.SetValue(clone, srcProperty.GetValue(source)); 
    } 
    //Clone all components from source and add them to the clone 
    if (source.components.Count > 0) 
    { 
     for (int i = source.components.Count - 1; i >= 0; i--) 
     { 
      var srcComp = source.components[i]; 
      var compClone = srcComp.CloneComponent(); 
      clone.components.Add(compClone); 
     } 
    } 

    clone.position = position; 
    clone.scale = scale; 
    AllGameObjects.Add(clone); 

    if (clone.components.Count > 0) 
    { 
     //Set the owner gameobjects and start the components 
     for (int i = clone.components.Count - 1; i >= 0; i--) 
     { 
      var comp = clone.components[i]; 
      comp.gameObject = clone; 
      comp.Start(); 
     } 
    } 

    return clone; 
} 

la méthode CloneComponent() de la ligne 17 var compClone = srcComp.CloneComponent(); est une méthode d'extension générique qui ressemble à ceci:

public static TComponent CloneComponent<TComponent>(this TComponent source) where TComponent : Component, new() 
{ 
    TComponent clone = new TComponent(); 

    var srcProperties = TypeDescriptor.GetProperties(typeof(TComponent)).Cast<PropertyDescriptor>(); 

    foreach (var srcProperty in srcProperties) 
    { 
     srcProperty.SetValue(clone, srcProperty.GetValue(source)); 
    } 

    return clone; 
} 

Cette méthode fonctionne bien, si la source n'a pas été tirée d'une liste de Component car il semble convertir tout type qu'il était au type racine ce qu'il fait seulement lorsqu'il est utilisé comme argument. Il en résulte évidemment TComponent étant de type Component et au lieu de cloner la source, il le convertit simplement en type racine et ne fait que cloner les propriétés de celui-ci.

Ce que je demande est pour un moyen de contourner ceci, ou pour qu'il ne soit pas converti en type racine lorsqu'il est utilisé comme argument?

Edit:

Le type du paramètre source sera de type correct, mais TComponent sera toujours de type Component lorsque l'argument source est donnée comme un argument dans une liste. Vous pourriez penser que je l'explique incorrectement mais je pense que c'est aussi bizarre que vous. La seule chose à laquelle je peux penser est que c'est un bug, ce qui est peu probable.

modifier final:
Merci beaucoup @Philipp pour la suggestion d'utiliser Activator.CreateInstance. Voici comment je l'ai changé ma méthode CloneComponent():

public static Component CloneComponent(this Component source) 
{ 
    var clone = Activator.CreateInstance(source.GetType()); 

    var srcProperties = TypeDescriptor.GetProperties(source.GetType()).Cast<PropertyDescriptor>(); 

    foreach (var srcProperty in srcProperties) 
    { 
     srcProperty.SetValue(clone, srcProperty.GetValue(source)); 
    } 

    return (Component)clone; 
} 

Répondre

1

source.GetType() au lieu de typeof(TComponent) doit retourner le réel (inherited) le type et vous pouvez créer une instance à l'aide Activator.CreateInstance(). De plus, vous pouvez toujours sérialiser en binaire l'arbre entier et le désérialiser à nouveau, ce qui vous donne un clone complet complet du graphe d'objets.

+0

Maintenant que vous le mentionnez, j'ai besoin de faire un edit - La source est du type correct mais TComponent est toujours Component ONLY quand l'argument provient d'une liste. Cela signifie que 'clone' est de type' Component', donc si j'utilise 'source.GetType()' il me donnera une erreur car il essayera d'assigner des valeurs aux propriétés que 'clone' n'a pas.J'ai vraiment du mal à expliquer ce qui se passe ici car cela semble être un résultat très bizarre ... Ce qui est bizarre, c'est que partout où j'appelle 'CloneComponent',' TComponent' sera correct sauf si l'argument provient d'une liste ... –

+0

La sérialisation binaire n'est pas non plus une option puisque j'utilise le framework xna et que je ne peux pas faire des types comme 'Vector2' serializable. –

+0

Cependant, en utilisant Activator.CreateInstance() dans 'CloneComponent' a résolu mon problème, merci beaucoup mon bon monsieur et passez une merveilleuse journée! –