2009-07-07 6 views
19

У меня есть класс A и класс B, который наследует класс A и расширяет его еще несколькими полями.Как «клонировать» объект в объект подкласса?

Имея объект a типа A, как я могу создать объект b типа B, который содержит все данные, которые возражали a содержали?

Я пробовал a.MemberwiseClone(), но это только дает мне другой тип A объект. И я не могу отличить A в B, так как отношение наследования допускает только противоположную передачу.

Каков правильный способ сделать это?

+0

Спасибо за ответы. Я искал автоматический способ этого, но вы предполагаете, что такого нет. :( – Vizu

+1

К сожалению, нет. Вам нужно будет добавить конструктор или фабричный метод некоторой формы. –

+0

@Vizu, какой метод вы приняли, я также хочу что-то подобное, пожалуйста, поместите здесь, если у вас есть –

ответ

8

Там нет средств сделать это автоматически встроены в язык ...

Один из вариантов, чтобы добавить конструктор класса B, который принимает класс А в качестве аргумента.

Тогда вы могли бы сделать:

B newB = new B(myA); 

Конструктор может просто скопировать соответствующие данные по мере необходимости, в этом случае.

+0

Можно ли копировать подкласс для суперкласса, имеющий некоторые из подобных feilds –

1

Создайте ctor в B, который позволяет передавать объект типа A, затем скопируйте поля A и установите соответствующие поля B.

0

Вы можете сделать метод Convert для класса B, который принимает базовый класс.

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

У вас также может быть конструктор класса ClassB для объекта ClassA.

11

Я бы добавил конструктор копирования к A, а затем добавлю новый конструктор в B, который берет экземпляр A и передает его в конструктор копии базы.

+2

Принятый ответ - это то, что я всегда делал, но этот поворот прост и изящен. – JMD

0

Нет, вы не можете этого сделать. Один из способов добиться этого - добавить конструктор класса B, который принимает параметр типа B, и добавлять данные вручную.

Таким образом, вы могли бы иметь что-то вроде этого:

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

Я не согласен с тем, что у вас должен быть метод Clone() для A, который возвращает B, поскольку это вводит круговую зависимость. –

+0

Я согласен с конструктором класса B, но зачем вам CloneToB() метод? –

+0

ehm да, вы правы, я не должен был включать этот метод. Я включил его, потому что плакат упоминал MemberWiseCl один(). Во всяком случае, этот код не очень хорош, и я его удалю. Благодарю. – Razzie

2

Использование 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

Вы можете добиться этого с помощью отражения.

Преимущество: Ремонтопригодность. Не нужно менять конструктор-копир или аналогичный, добавляя или удаляя свойства.

Недостаток: Производительность. Отражение происходит медленно. Мы все еще говорим миллисекунды на средних классах.

Вот отражение на основе мелкой реализации копия поддержки копирования в подклассе, используя методы расширения:

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; 
} 

Этот метод копирует все свойства - частные и государственные, а также все поля. Свойства копируются по ссылке, делая ее мелкой копией.

испытание Единица измерения:

[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

Что считается средним размером класса? –

+0

@ adam-l-s Хороший вопрос. Мой ответ, как всегда, касается производительности: Мера. Если это достаточно быстро для вас, используйте его. Отражение считается в 1000 раз медленнее, чем доступ к свойствам обычным способом: http://stackoverflow.com/questions/25458/how-costly-is-net-reflection – Nilzor

0

В вашем базовом классе добавить виртуальный метод CreateObject ниже ...

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; 
    } 

то, что вы хотите создать реальную жизнь объект подкласса от суперкласса только звонок

this.CreateObject<subclass>(); 

Это должно быть сделано!

0

Хотя никто не предлагал это (и это не будет работать для всех, правда), следует сказать, что если у вас есть возможность создать объект b из get-go, сделайте это вместо создания объекта a копирование объекта b. Например, представьте, что вы находитесь в одной и той же функции и имеют следующий код:

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... 

Вместо того, чтобы беспокоиться о том, что последний комментировал шаг, просто начать с б и установить значения:

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

Пожалуйста, не Так что, если ты думаешь, что это глупо! Я столкнулся с множеством программистов, которые настолько сосредоточены на своем коде, что не понимают, что более простое решение смотрит на них в лицо. :)

Смежные вопросы