2010-06-30 6 views
3

У меня есть определенная хитрость классов, которая нуждается в возможности для копирования всех общедоступных свойств из одного объекта в другой.
Каждый класс имеет определенный набор публичных свойств, который может быть может отличаться от любого другого класса.Копирование объектов в этот «объект» в C#

Пример:

class Base 
{ 
    // Common properties/methods... 
    public void Copy<T>(T data) where T : Base 
    { 
    // ... 
    } 
} 

class D1 : Base 
{ 
    public int ID 
    { 
    get; 
    set; 
    } 
} 

class D2 : Base 
{ 
    public string Name 
    { 
    get; 
    set; 
    } 
} 

Через прибегая к помощи я прочитал об этих методах:

  • с помощью отражения
  • Генерирование IL код
  • Сериализация

Все из них либо очень c omplex или очень медленно или иногда и то, и другое.
Я что-то упустил? Есть ли другой способ доступа к необработанному указателю this?

EDIT:
Я сделаю это.
T относится к типу вызывающего класса. Например, если он был вызван D1 T, всегда будет D1.
Причина общего заключается в том, что я не знаю, что такое Т.
Я что-то упустил?
Должен ли я использовать Base data в качестве параметра?

+1

Итак, как бы вы скопировали 'D1.ID' в' D2.Name'? – Oded

+0

Похоже, это может помочь, http://stackoverflow.com/questions/1198886/c-using-reflection-to-copy-base-class-properties. – Noldorin

+0

@Oded: Вы не будете. Копирование из D1 всегда будет использоваться для объекта D1. Я просто хочу ** общий метод **, который сможет копировать объект T с этим типом T. Этот метод должен быть открыт базой. –

ответ

1

Метод Copy в классе Base только имеет доступ к объектам, определенным в классе Base. Вы можете использовать .

Но вы не можете копировать свойства из подклассов, не используя что-то вроде отражения. Но даже с отражением вам нужно какое-то знание о отображении свойств между различными подклассами, например копирование свойства ID на Name.

Таким образом, вам нужно будет написать отдельные реализации для каждого (разрешенного) преобразования подкласса.

public interface IBaseCopier<TFrom, TTo> where TFrom : Base, TTo : Base 
{ 
    void Copy(TFrom from, TTo to); 
} 

public class D1ToD2Copier : IBaseCopier<D1, D2> 
{ 
    public void Copy(D1 from, D2 to) 
    { 
    // Copy properties from the D1 instance to the D2 instance. 
    } 
} 

Вы можете зарегистрировать все ICopier<TFrom, TTo> реализации в классе фабрики. Этот класс будет искать реализацию копира на основе аргументов типа. Если нет копира для определенной комбинации типов, то есть преобразование не поддерживается, фабрика должна исключить исключение.

public class CopierFactory 
{ 
    public ICopier<TFrom, TTo> Create<TFrom, TTo>() where TFrom : Base, TTo : Base 
    { 
    // Look up the ICopier implementation for the given types. 
    } 
} 

Редактировать

Вы можете использовать метод MemberwiseClone, чтобы создать копию объекта.

public class Base 
{ 
    public static T Copy<T>(T data) where T : Base 
    { 
    return data.MemberwiseClone() as T; 
    } 
} 

Если вам нужно больше контроля над клонированием, вы можете реализовать ICloneable interface.

Примечание: Вы должны понимать, что вы не можете клонировать D1 экземпляр в D2 инстанции. Это было бы похоже на клонирование овцы на лошади.

+0

Можно ли просто использовать копию Ctor, так как я просто копирую из T в это тип T? –

+0

Вы имеете в виду что-то вроде 'public D1 (D2 data) {}', где конструктор 'D1' будет копировать данные из экземпляра' D2'? –

+0

Вы, ребята, до сих пор этого не понимаете. Проблема в том, что я не могу сделать это = данные; // typeof (data) == typeof (this), оба наследуются от базы. Итак, это будет что-то вроде D1 (данные D1) {} Проблема в том, как мне назначить данные? –

6

Что вам не хватает, так это то, что вы просите компилятор знать, что T может быть одним из типов D1 и D2, когда все, что вы сказали, это то, что T является базой. Как он мог знать, какие свойства или даже тип вашего объекта, так как эта информация известна только во время выполнения. Даже если вы могли бы go foreach (PropertyInfo in this.Properties), это выяснит название этих свойств во время выполнения, так что будьте столь же медленными, как и Reflection, потому что, как еще это возможно? (это - отражение, только более красивый синтаксис). Он не может знать, какие свойства являются общими до тех пор, пока он не узнает, с какими типами он имеет дело, и вы сказали: «Я не говорю вам до времени исполнения», поэтому ответ «хорошо, мне нужно будет взглянуть на время выполнения», то есть на размышление.

Во-вторых, только потому, что D1 и D2 могут иметь свойство с именем «Размер», это не значит, что они являются одним и тем же свойством (если это свойство отсутствует у общего предка).

Например,

  • ArtilleryAmmo.Shell и PecanNut.Shell.
  • AcmeCorp.Staff и GandolfTheWizard.Staff
  • California.State и MyBrokenEngine.State
  • LoudSpeaker.Volume и MassiveCrater.Volume
  • Cave.Bats и BasketballGame.Bats

и т.д. и т.п.

+1

+1 - Отличные примеры –

2

Вы можете обойти это с помощью архитектурных изменений и использовать «PropertyBag» для хранения свойств каждого класса.

A PropertyBag - это, по сути, Dictionary<string, object>, где вы можете дать кусочек данных имя и добавить его в сумку. Недостаток заключается в том, что все становится отличным до object, поэтому он не очень безопасен и плюс много бокса/распаковки, а также строки, поскольку имена не проверяются во время компиляции, поэтому опечатки являются постоянной угрозой.

При определении свойства на классе, вы храните/извлечь элемент из класса PropertyBag:

public int MyProperty 
{ 
    get 
    { 
     return (int)_propertyBag["MyProperty"]; 
    } 
    set 
    { 
     if(_propertyBag.Keys.Contains("MyProperty")) 
     { 
      _propertyBag["MyProperty"] = value; 
     } 
     else 
     { 
      _propertyBag.Add("MyProperty", value); 
     } 
    } 
} 

Так что теперь агрегировать все свойства производных классов, вы можете выставить их «сырой 'PropertyBag и повторить его.

Как я уже говорил, PropertyBags не относятся к типу, поэтому у вас есть два класса в иерархии с тем же именем свойства, но с другим типом, и вы попадаете в неприятности.

EDIT: Если вы заинтересованы в производительности, вам придется реализовать это несколько способов и перенести тестирование различных реализаций - я не могу честно сказать, что PropertyBag будет на самом деле быстрее, чем использование отражения.

1

Я думаю, что метод копирования должен наследоваться производными классами D1, D2 и их обязанностью копировать свои собственные свойства в/из других типов.

+1

Это создаст много связей между подклассами. –

0

Что я хотел бы сделать, это создать extension method для класса Base, как:

namespace ExtensionMethods 
{ 
    public static class MyExtensions 
    { 
     public static int CopyTo<T>(this Base source, ref T dest) 
     { 
      // Use reflection to cycle public properties and if you find equally named ones, copy them. 
     } 
    } 
} 

Тогда вы могли бы назвать это в ваших объектов и как:

source.CopyTo<ClassType>(ref this); 

Я не проверял, так не уверен, что он будет работать точно так же, как описано. Я сделал что-то похожее на Cast DataRows на Entities в большом проекте, над которым я работал.

+0

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

+1

Кроме того, вы не можете подтвердить это, так как он только что прочитан. –