2009-07-07 7 views
19

J'ai une classe A et une classe B qui hérite de la classe A et l'étend avec d'autres champs.Comment "cloner" un objet dans un objet de sous-classe?

Avoir un objet a de type A, comment puis-je créer un objet b de type B qui contient toutes les données, cet objet a contenu? J'ai essayé a.MemberwiseClone() mais cela me donne seulement un autre objet de type A. Et je ne peux pas lancer A en B car la relation d'héritage autorise uniquement la conversion opposée.

Quelle est la bonne façon de procéder?

+0

Merci pour les réponses. Je cherchais un moyen automatique de le faire mais vous suggérez qu'il n'y en a pas. :( – Vizu

+1

Malheureusement, vous aurez besoin d'ajouter un constructeur ou une méthode d'usine d'une certaine forme –

+0

@Vizu quelle méthode avez-vous adopté, je veux aussi quelque chose de similaire, s'il vous plaît mettez ici si vous l'avez –

Répondre

8

Il n'y a pas moyen de le faire automatiquement intégré dans la langue ...

Une option est d'ajouter un constructeur à la classe B qui prend une classe A comme argument.

Ensuite, vous pouvez faire:

B newB = new B(myA); 

Le constructeur peut simplement copier les données pertinentes sur au besoin, dans ce cas.

+0

est-il possible de copier une sous classe à super classe ayant certains des feilds similaires –

1

Créez un ctor dans B qui permet de passer dans un objet de type A, puis copiez les champs A et définissez les champs B selon le cas.

0

Vous pouvez créer une méthode Convert sur la classe B qui prend en compte la classe de base.

public ClassB Convert(ClassA a) 
{ 
    ClassB b = new ClassB(); 
    // Set the properties 
    return b; 
} 

Vous pouvez également avoir un constructeur pour ClassB prendre un objet de ClassA.

11

Je voudrais ajouter un constructeur de copie à A, puis ajouter un nouveau constructeur à B qui prend une instance de A et le passe au constructeur de copie de la base.

+2

La réponse acceptée est ce que j'ai toujours fait, mais – JMD

0

Non, vous ne pouvez pas faire cela. Une façon d'y parvenir est d'ajouter un constructeur sur la classe B qui accepte un paramètre de type B, et d'ajouter des données manuellement.

Donc, vous pourriez avoir quelque chose comme ceci:

public class B 
{ 
    public B(A a) 
    { 
    this.Foo = a.foo; 
    this.Bar = a.bar; 
    // add some B-specific data here 
    } 
} 
+3

Je ne suis pas d'accord sur le fait que vous devriez avoir une méthode Clone() sur A qui retourne un B, car cela introduit une dépendance circulaire –

+0

Je suis d'accord avec le constructeur pour la classe B, mais pourquoi avez-vous besoin du CloneToB?() méthode? –

+0

ehm ouais vous avez raison, je n'aurais pas dû inclure cette méthode, je l'ai inclus parce que l'affiche mentionné MemberWiseCl un(). Quoi qu'il en soit, ce code n'est pas bon et je vais le supprimer. Merci. – Razzie

2

En utilisant Factory Method Pattern:

private abstract class A 
    { 
     public int A1 { get; set; } 

     public abstract A CreateInstance(); 

     public virtual A Clone() 
     { 
      var instance = CreateInstance(); 
      instance.A1 = A1; 
      return instance; 
     } 
    } 

    private class B : A 
    { 
     public int A3 { get; set; } 

     public override A CreateInstance() 
     { 
      return new B(); 
     } 

     public override A Clone() 
     { 
      var result = (B) base.Clone(); 
      result.A3 = A3; 
      return result; 
     } 
    } 

    private static void Main(string[] args) 
    { 
     var b = new B() { A1 = 1, A3 = 2 }; 

     var c = b.Clone(); 
    } 
3

Vous pouvez y parvenir en utilisant la réflexion.

Avantage: Maintenabilité. Pas besoin de changer de constructeur de copie ou similaire, en ajoutant ou en supprimant des propriétés.

Inconvénient: Performances. La réflexion est lente. Nous parlons encore des millisecondes sur des classes moyennes.

Voilà une réflexion basée sur peu profonde mise en œuvre de copie de soutien copie à la sous-classe, en utilisant des méthodes d'extension:

public static TOut GetShallowCopyByReflection<TOut>(this Object objIn) 
{ 
    Type inputType = objIn.GetType(); 
    Type outputType = typeof(TOut); 
    if (!outputType.Equals(inputType) && !outputType.IsSubclassOf(inputType)) throw new ArgumentException(String.Format("{0} is not a sublcass of {1}", outputType, inputType)); 
    PropertyInfo[] properties = inputType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); 
    FieldInfo[] fields = inputType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); 
    TOut objOut = (TOut)Activator.CreateInstance(typeof(TOut)); 
    foreach (PropertyInfo property in properties) 
    { 
     try 
     { 
      property.SetValue(objIn, property.GetValue(objIn, null), null); 
     } 
     catch (ArgumentException) { } // For Get-only-properties 
    } 
    foreach (FieldInfo field in fields) 
    { 
     field.SetValue(objOut, field.GetValue(objIn)); 
    } 
    return objOut; 
} 

Cette méthode permet de copier toutes les propriétés - privées et publiques, ainsi que tous les domaines. Les propriétés sont copiées par référence, ce qui en fait une copie superficielle.

tests unitaires:

[TestClass] 
public class ExtensionTests { 
    [TestMethod] 
    public void GetShallowCloneByReflection_PropsAndFields() 
    { 
     var uri = new Uri("http://www.stackoverflow.com"); 
     var source = new TestClassParent(); 
     source.SomePublicString = "Pu"; 
     source.SomePrivateString = "Pr"; 
     source.SomeInternalString = "I"; 
     source.SomeIntField = 6; 
     source.SomeList = new List<Uri>() { uri }; 

     var dest = source.GetShallowCopyByReflection<TestClassChild>(); 
     Assert.AreEqual("Pu", dest.SomePublicString); 
     Assert.AreEqual("Pr", dest.SomePrivateString); 
     Assert.AreEqual("I", dest.SomeInternalString); 
     Assert.AreEqual(6, dest.SomeIntField); 
     Assert.AreSame(source.SomeList, dest.SomeList); 
     Assert.AreSame(uri, dest.SomeList[0]);    
    } 
} 

internal class TestClassParent 
{ 
    public String SomePublicString { get; set; } 
    internal String SomeInternalString { get; set; } 
    internal String SomePrivateString { get; set; } 
    public String SomeGetOnlyString { get { return "Get"; } } 
    internal List<Uri> SomeList { get; set; } 
    internal int SomeIntField; 
} 

internal class TestClassChild : TestClassParent {} 
+0

Qu'est-ce qui compte comme taille moyenne des classes? –

+0

@ adam-l-s Hehe bonne question. Ma réponse comme toujours quand il s'agit de performance: Mesurer. Si c'est assez rapide pour vous, utilisez-le. On dit que la réflexion est 1000 fois plus lente que d'accéder aux propriétés normalement: http://stackoverflow.com/questions/25458/how-costly-is-net-reflection – Nilzor

0

Dans votre classe de base ajouter la méthode virtuelle CreateObject ci-dessous ...

public virtual T CreateObject<T>() 
    { 
     if (typeof(T).IsSubclassOf(this.GetType())) 
     { 
      throw new InvalidCastException(this.GetType().ToString() + " does not inherit from " + typeof(T).ToString()); 
     } 

     T ret = System.Activator.CreateInstance<T>(); 

     PropertyInfo[] propTo = ret.GetType().GetProperties(); 
     PropertyInfo[] propFrom = this.GetType().GetProperties(); 

     // for each property check whether this data item has an equivalent property 
     // and copy over the property values as neccesary. 
     foreach (PropertyInfo propT in propTo) 
     { 
      foreach (PropertyInfo propF in propFrom) 
      { 
       if (propT.Name == propF.Name) 
       { 
        propF.SetValue(ret,propF.GetValue(this)); 
        break; 
       } 
      } 
     } 

     return ret; 
    } 

disent alors vous voulez créer un objet réel de la sous-classe de vie de la classe super juste appelez

this.CreateObject<subclass>(); 

Cela devrait le faire!

0

Alors que personne ne l'a suggéré (et cela ne marchera pas pour tout le monde, certes), il faut dire que si vous avez la possibilité de créer l'objet b dès le départ, faites cela plutôt que de créer un objet copier sur l'objet b. Par exemple, imaginez que vous êtes dans la même fonction et avoir ce code:

var a = new A(); 
a.prop1 = "value"; 
a.prop2 = "value"; 
... 
// now you need a B object instance... 
var b = new B(); 
// now you need to copy a into b... 

Au lieu de se soucier de cette dernière étape a commenté, il suffit de commencer avec b et définir les valeurs:

var b = new B(); 
b.prop1 = "value"; 
b.prop2 = "value"; 

S'il vous plaît enfiler Ne me rabaisse pas parce que tu penses que ce qui précède est stupide! J'ai rencontré beaucoup de programmeurs qui sont tellement concentrés sur leur code qu'ils n'ont pas réalisé qu'une solution plus simple les regardait en face. :)

Questions connexes