2012-12-23 14 views
7

Po pierwsze, prawdopodobnie wszyscy zgadzają się, że najlepszym sposobem byłoby zaimplementowanie funkcji kopiowania wewnątrz niestandardowego obiektu/obiektu. Ale rozważ ten scenariusz. Nie mamy tej opcji i nie chcemy pisać konkretnej funkcji, która będzie dokładną kopią encji, ponieważ jednostka zostanie zmieniona w przyszłości, więc nasza funkcja kopiowania zakończy się niepowodzeniem.Najszybszy sposób kopiowania dynamicznego obiektu, który nie obsługuje funkcji kopiowania

Tutaj jest uproszczona wersja bieżącej jednostki:

[Serializable] 
class MyEntity 
{ 
    public MyEntity() 
    { 
    } 

    public MyEntity(int id, string name) 
    { 
     this.Id = id; 
     this.Name = name; 
    } 

    public int Id { get; set; } 

    public string Name { get; set; } 

    public MyEntity Copy() 
    { 
     throw new NotImplementedException(); 
    } 
} 

by sprostać wszystkim wymaganiom powyżej wymyśliłem dwa rozwiązania:

 //original... 
     MyEntity original = new MyEntity() { Id = 1, Name = "demo1" }; 

     //first way to copy object... 
     List<MyEntity> list = new List<MyEntity>() { original}; 
     MyEntity copy1 = list.ConvertAll(entity => new MyEntity(entity.Id, entity.Name))[0]; 

     //second way to copy object... 
     byte[] bytes = SerializeEntity(original); 
     MyEntity copy2 = (MyEntity)DeserializeData(bytes); 


    byte[] SerializeEntity(object data) 
    { 
     byte[] result = null; 
     using (MemoryStream ms = new MemoryStream()) 
     { 
      BinaryFormatter formatter = new BinaryFormatter(); 
      formatter.Serialize(ms, data); 
      result = ms.ToArray(); 
     } 
     return result; 
    } 

    object DeserializeData(byte[] data) 
    { 
     object result = null; 
     using(MemoryStream ms = new MemoryStream(data)) 
     { 
      BinaryFormatter formatter = new BinaryFormatter(); 
      result = formatter.Deserialize(ms); 
     } 
     return result; 
    } 

i teraz pytanie. Jakie rozwiązanie jest najbardziej optymalne dla sceny i dlaczego, pierwsze czy drugie? Czy istnieje lepszy sposób na wykonanie dokładnej kopii uwzględniającej powyższe wymagania? Kopie będą wykonywane w dużych ilościach.

PS uwaga: Zdaję sobie sprawę, że pierwszy z nich jest w zasadzie już funkcji kopiowania jako Honza zauważył. W pewnym sensie poszukuję czegoś uniwersalnego, jak seryjna i szybka jak niestandardowa funkcja kopiowania.

Odpowiedz

1

można spróbować użyć AutoMapper:

Mapper.CreateMap<MyEntity, MyEntity>(); 

... 

var copy3 = Mapper.Map<MyEntity, MyEntity>(original); 
+0

Przeczytałem kilka postów na temat AutoMappera, ale nie mogłem wywnioskować z tego, co można zrobić w moim przypadku. Niektórzy twierdzą, że nie jest to najlepsze rozwiązanie we wszystkich przypadkach. Czy mógłbyś zaproponować jakieś wskazówki, które mogą skutkować złym działaniem przy użyciu AutoMappera? +1 dla alternatywy w tej chwili –

7

Po pierwsze prawdopodobnie wszyscy zgadzają się, że najlepszym sposobem byłoby zaimplementowanie funkcji Kopiuj wewnątrz niestandardowego obiektu/obiektu.

Nie zgadzam się. Nienawidzę pisać takich metod za każdym razem. Oto moja propozycja stosując metodę rozszerzenia:

public static T Copy<T>(this T obj) 
    where T : class 
{ 
    using (MemoryStream stream = new MemoryStream()) 
    { 
     BinaryFormatter formatter = new BinaryFormatter(); 
     formatter.Serialize(stream, obj); 

     stream.Seek(0, SeekOrigin.Begin); 
     return formatter.Deserialize(stream) as T; 
    } 
} 

Jest to w zasadzie twoje drugie rozwiązanie, ale lekko zoptymalizowany. Nie ma potrzeby kopiowania obiektu MemoryStream do tablicy bajtów, a następnie utworzenia z tego innego obiektu MemoryStream.

Najlepsze jest to, że jest ogólne i może być używane z każdym obiektem posiadającym atrybut [Serializable]. I jestem pewien, że jest szybszy niż pierwsze rozwiązanie, w którym musisz uzyskać dostęp do każdej nieruchomości (chociaż nie mierzyłem).

Edit:

Ok, faktycznie jakieś pomiary teraz. Moje pierwsze założenie dotyczące występu było całkowicie błędne!

stworzyłem 1000000 obiektów MyEntity z wartościami losowymi i następnie skopiowane je (ja również uważane wskazówkę Honza Brestan „s na głębokich i płytkich kopii):

głęboka kopia z Binary formater: 14.727 s
głęboka kopia metodą kopiowania : 0.490 s
płytkie kopia z refleksji: 5.499 s
płytkie kopia metodą kopiowania: 0,144 s

+0

To jest podobne do czegoś używamy jednak, być może warto zauważyć, że za każdym razem jest dostarczany inna „T” nowa metoda będzie musiała być wygenerowany przez kompilator. Spowoduje to nieznaczne obciążenie procesora i więcej pamięci. – MadSkunk

+0

@pescolino przy użyciu serializacji jest rzeczywiście najbardziej odporny na kulę, jeśli obiekt jest oznaczony jako możliwy do serializacji, ale jest również najwolniejszy. Naprawdę chciałem zrobić jakiś inteligentny i szybki sposób na zrobienie kopii, ale mam świadomość, że nie ma "darmowego lunchu". Zawsze tracisz coś, by zyskać coś innego. –

+0

@GregorPrimar Przeczytaj [Poprawa refleksji na temat wydajności, jakie alternatywy należy wziąć pod uwagę] (http://stackoverflow.com/questions/1027980/improving-performance-reflection-what-alternatives- mógłby rozważyć), a zwłaszcza [Blog Jona Skeeta ] (http://msmvps.com/blogs/jon_skeet/archive/2008/08/09/making-reflection-fly-and-exploring-delegates.aspx), o czym wspomniała w swojej odpowiedzi. – pescolino

1

Jaka jest różnica między pierwszą próbę i pisać własną metodę kopiowania?

public MyEntity Copy() 
{ 
    return new MyEntity(this.Id, this.Name); 
} 

Dla mnie to wygląda lepiej niż próba zbierania, który robi dokładnie to samo i tak - w obu przypadkach trzeba jawnie wymienić wszystkie właściwości.

Jeśli nie można modyfikować samej klasy podmiotu, nadal można utworzyć metodę rozszerzenia (umieszczony w statycznej klasie widocznym skąd chcesz użyć logiki kopiowania)

public static MyEntity Copy(this MyEntity source) 
{ 
    return new MyEntity(source.Id, source.Name); 
} 

Co do drugiej próbie, czy rozważałeś różnice między tymi dwoma? Nie są takie same. Najpierw tworzy się płytka płytka, podczas gdy druga (pod warunkiem, że całe drzewo obiektów można przekształcić do postaci szeregowej) generuje kopię głęboką. Różnica polega na tym, czy jego właściwości są kopiowane, czy też zarówno oryginalny obiekt, jak i jego kopia odnoszą się do tych samych obiektów. To samo dotyczy wersji pescolino, która wygląda bardzo ładnie BTW.

Pytanie więc, którą kopię chcesz/potrzebujesz.

Dla prawdziwie dynamicznej (ale prawdopodobnie nie całkiem skutecznej) metody kopiowania myślę, że będziesz potrzebował użyć odbicia, wyliczając wszystkie właściwości i kopiując ich wartości z oryginalnego obiektu do kopii. Non-pełna wersja demo może wyglądać następująco:

public static MyEntity Copy(this MyEntity source) 
{ 
    var result = new MyEntity(); 

    var properties = source.GetType().GetProperties(
      BindingFlags.Instance | BindingFlags.Public); 

    foreach (var property in properties) 
    { 
     var val = property.GetValue(source, null); 
     property.SetValue(result, val, null); 
    } 

    return result; 
} 

Takie podejście ma problemy z własnym, a mianowicie wydajności, sporadyczne trzeba obsłużyć szczególnych przypadkach (indeksujących, właściwości niepublicznych ...), ale dostanie praca jest wykonywana i działa również na obiektach nie nadających się do odpalenia. Wersja ogólna również byłaby łatwa do wykonania - to zależy od Ciebie, czy tego potrzebujesz.

Warto również zauważyć, ponieważ zarówno ja, jak i pescolino zaproponowaliśmy zastosowanie metod rozszerzeń, istnieje potencjalny problem z nimi. Jeśli twój obiekt rzeczywiście zawiera metodę Copy z tym samym podpisem co rozszerzenie, kompilator zdecyduje się użyć go zamiast rozszerzenia. To oczywiście rzuci NotImplementedException po wywołaniu. Jeśli tak jest (i nie jest to tylko przykładowy kod), może to być poważna "gotcha". Jedynym rozwiązaniem w takim przypadku jest zmiana sygnatury metody rozszerzenia, najlepiej poprzez zmianę jej nazwy.

+0

Zgadzam się z tobą, to nie jest duża różnica. Jedynym "jasnym" punktem jest to, że otrzymam wyjątek, jeśli konstruktor zostanie zmieniony. Przede wszystkim chciałem napisać najszybszą możliwą opcję, utworzyć nową instancję i ustawić indywidualne właściwości. Jednak w tym podejściu nie uzyskałbym wyjątku, gdyby wprowadzono nową właściwość. Zasadniczo robi nieprawidłową kopię, nie wiedząc o tym. Twoja próba oczywiście obejmuje ten problem. –

Powiązane problemy