C #에서 일반 목록을 어떻게 복제합니까?
C #에 일반 객체 목록이 있고 목록을 복제하려고합니다. 목록의 항목은 복제 가능하지만 수행 할 수있는 옵션이없는 것 같습니다 list.Clone()
.
이 문제를 해결하는 쉬운 방법이 있습니까?
확장 방법을 사용할 수 있습니다.
static class Extensions
{
public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
}
요소가 값 유형 인 경우 다음을 수행 할 수 있습니다.
List<YourType> newList = new List<YourType>(oldList);
그러나 참조 유형이고 딥 카피를 원한다면 (요소가 올바르게 구현되었다고 가정 ICloneable
) 다음과 같이 할 수 있습니다.
List<ICloneable> oldList = new List<ICloneable>();
List<ICloneable> newList = new List<ICloneable>(oldList.Count);
oldList.ForEach((item) =>
{
newList.Add((ICloneable)item.Clone());
});
분명히 ICloneable
위의 제네릭을 대체 하고 구현하는 요소 유형으로 캐스팅하십시오 ICloneable
.
요소 유형이 지원하지 않지만 ICloneable
복사 생성자가있는 경우 대신 다음을 수행 할 수 있습니다.
List<YourType> oldList = new List<YourType>();
List<YourType> newList = new List<YourType>(oldList.Count);
oldList.ForEach((item)=>
{
newList.Add(new YourType(item));
});
개인적으로, 나는 ICloneable
모든 회원의 깊은 사본을 보장 할 필요가 있기 때문에 피할 것 입니다. 대신, 복사 생성자 또는 이와 같은 팩토리 메소드 YourType.CopyFrom(YourType itemToCopy)
가의 새로운 인스턴스를 반환하도록 제안합니다 YourType
.
이러한 옵션은 메소드 (확장 또는 기타)로 랩핑 할 수 있습니다.
public static object DeepClone(object obj)
{
object objResult = null;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, obj);
ms.Position = 0;
objResult = bf.Deserialize(ms);
}
return objResult;
}
이것은 C # 및 .NET 2.0으로 수행하는 한 가지 방법입니다. 귀하의 개체는이어야 [Serializable()]
합니다. 목표는 모든 참조를 잃어 버리고 새로운 참조를 만드는 것입니다.
얕은 복사본의 경우 대신 일반 List 클래스의 GetRange 메서드를 사용할 수 있습니다.
List<int> oldList = new List<int>( );
// Populate oldList...
List<int> newList = oldList.GetRange(0, oldList.Count);
인용 : Generics Recipes
약간 수정 한 후 다음을 복제 할 수도 있습니다.
public static T DeepClone<T>(T obj)
{
T objResult;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, obj);
ms.Position = 0;
objResult = (T)bf.Deserialize(ms);
}
return objResult;
}
목록을 복제하려면 .ToList ()를 호출하십시오.
Microsoft (R) Roslyn C# Compiler version 2.3.2.62116
Loading context from 'CSharpInteractive.rsp'.
Type "#help" for more information.
> var x = new List<int>() { 3, 4 };
> var y = x.ToList();
> x.Add(5)
> x
List<int>(3) { 3, 4, 5 }
> y
List<int>(2) { 3, 4 }
>
에있는 모든 단일 객체의 실제 복제가 필요하지 않은 경우 List<T>
목록을 복제하는 가장 좋은 방법은 이전 목록을 collection 매개 변수로 사용하여 새 목록을 만드는 것입니다.
List<T> myList = ...;
List<T> cloneOfMyList = new List<T>(myList);
myList
삽입 또는 제거와 같은 변경 은 영향을 미치지 cloneOfMyList
않으며 그 반대도 마찬가지입니다.
그러나 두 목록에 포함 된 실제 개체는 여전히 동일합니다.
가치 유형에만 관심이 있다면 ...
그리고 당신은 유형을 알고 있습니다 :
List<int> newList = new List<int>(oldList);
이전에 유형을 모르는 경우 도우미 기능이 필요합니다.
List<T> Clone<T>(IEnumerable<T> oldList)
{
return newList = new List<T>(oldList);
}
그냥 :
List<string> myNewList = Clone(myOldList);
AutoMapper (또는 원하는 매핑 라이브러리)를 사용하여 복제하는 것은 간단하고 유지 관리가 쉽습니다.
맵핑을 정의하십시오.
Mapper.CreateMap<YourType, YourType>();
마술을하십시오 :
YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);
프로젝트에서 Newtonsoft.Json을 이미 참조했고 객체를 직렬화 할 수 있다면 항상 다음을 사용할 수 있습니다.
List<T> newList = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(listToCopy))
아마도 가장 효율적인 방법은 아니지만 100 ~ 1000 번을 수행하지 않으면 속도 차이를 알지 못할 수도 있습니다.
public static Object CloneType(Object objtype)
{
Object lstfinal = new Object();
using (MemoryStream memStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
binaryFormatter.Serialize(memStream, objtype); memStream.Seek(0, SeekOrigin.Begin);
lstfinal = binaryFormatter.Deserialize(memStream);
}
return lstfinal;
}
public class CloneableList<T> : List<T>, ICloneable where T : ICloneable
{
public object Clone()
{
var clone = new List<T>();
ForEach(item => clone.Add((T)item.Clone()));
return clone;
}
}
public List<TEntity> Clone<TEntity>(List<TEntity> o1List) where TEntity : class , new()
{
List<TEntity> retList = new List<TEntity>();
try
{
Type sourceType = typeof(TEntity);
foreach(var o1 in o1List)
{
TEntity o2 = new TEntity();
foreach (PropertyInfo propInfo in (sourceType.GetProperties()))
{
var val = propInfo.GetValue(o1, null);
propInfo.SetValue(o2, val);
}
retList.Add(o2);
}
return retList;
}
catch
{
return retList;
}
}
을 사용하여 간단히 목록을 배열로 변환 한 다음을 사용하여 배열 ToArray
을 복제 할 수도 있습니다 Array.Clone(...)
. 필요에 따라 Array 클래스에 포함 된 메소드가 요구를 충족시킬 수 있습니다.
확장 방법을 사용할 수 있습니다 :
namespace extension
{
public class ext
{
public static List<double> clone(this List<double> t)
{
List<double> kop = new List<double>();
int x;
for (x = 0; x < t.Count; x++)
{
kop.Add(t[x]);
}
return kop;
}
};
}
예를 들어 값 유형 멤버를 사용하여 모든 오브젝트를 복제 할 수 있습니다.이 클래스를 고려하십시오.
public class matrix
{
public List<List<double>> mat;
public int rows,cols;
public matrix clone()
{
// create new object
matrix copy = new matrix();
// firstly I can directly copy rows and cols because they are value types
copy.rows = this.rows;
copy.cols = this.cols;
// but now I can no t directly copy mat because it is not value type so
int x;
// I assume I have clone method for List<double>
for(x=0;x<this.mat.count;x++)
{
copy.mat.Add(this.mat[x].clone());
}
// then mat is cloned
return copy; // and copy of original is returned
}
};
참고 : 복사 (또는 복제)를 변경해도 원래 개체에는 영향을 미치지 않습니다.
동일한 용량의 복제 된 목록이 필요한 경우 다음을 시도해보십시오.
public static List<T> Clone<T>(this List<T> oldList)
{
var newList = new List<T>(oldList.Capacity);
newList.AddRange(oldList);
return newList;
}
내 친구 Gregor Martinovic와 저는 JavaScript Serializer를 사용하여이 쉬운 솔루션을 생각해 냈습니다. 클래스를 Serializable로 플래그 지정할 필요가 없으며 Newtonsoft JsonSerializer를 사용한 테스트에서 BinaryFormatter를 사용하는 것보다 훨씬 빠릅니다. 모든 객체에 확장 방법을 사용할 수 있습니다.
표준 .NET JavascriptSerializer 옵션 :
public static T DeepCopy<T>(this T value)
{
JavaScriptSerializer js = new JavaScriptSerializer();
string json = js.Serialize(value);
return js.Deserialize<T>(json);
}
Newtonsoft JSON을 사용하는 더 빠른 옵션 :
public static T DeepCopy<T>(this T value)
{
string json = JsonConvert.SerializeObject(value);
return JsonConvert.DeserializeObject<T>(json);
}
IClonable을 구현하지 않는 항목의 ICollection을 변환하는 확장 기능을 직접 만들었습니다.
static class CollectionExtensions
{
public static ICollection<T> Clone<T>(this ICollection<T> listToClone)
{
var array = new T[listToClone.Count];
listToClone.CopyTo(array,0);
return array.ToList();
}
}
automapper를 사용하여 객체를 복사합니다. 방금 하나의 객체를 자체에 매핑하는 매핑을 설정했습니다. 이 작업을 원하는 방식으로 감쌀 수 있습니다.
http://automapper.codeplex.com/
//try this
List<string> ListCopy= new List<string>(OldList);
//or try
List<T> ListCopy=OldList.ToList();
아무도 이것을 읽지 않으면 운이 좋을 것입니다 ...하지만 Clone 메소드에서 유형 객체 목록을 반환하지 않기 위해 인터페이스를 만들었습니다.
public interface IMyCloneable<T>
{
T Clone();
}
그런 다음 확장명을 지정했습니다.
public static List<T> Clone<T>(this List<T> listToClone) where T : IMyCloneable<T>
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
그리고 여기 내 A / V 마킹 소프트웨어의 인터페이스 구현이 있습니다. Clone () 메서드가 VidMark 목록을 반환하도록하고 싶었습니다 (ICloneable 인터페이스에서 메서드가 개체 목록을 반환하도록하려는 경우).
public class VidMark : IMyCloneable<VidMark>
{
public long Beg { get; set; }
public long End { get; set; }
public string Desc { get; set; }
public int Rank { get; set; } = 0;
public VidMark Clone()
{
return (VidMark)this.MemberwiseClone();
}
}
마지막으로 클래스 내 확장의 사용법 :
private List<VidMark> _VidMarks;
private List<VidMark> _UndoVidMarks;
//Other methods instantiate and fill the lists
private void SetUndoVidMarks()
{
_UndoVidMarks = _VidMarks.Clone();
}
그것을 좋아하는 사람? 개선 사항이 있습니까?
이 경우 캐스트를 사용하면 얕은 사본에 도움이 될 수 있습니다.
IList CloneList(IList list)
{
IList result;
result = (IList)Activator.CreateInstance(list.GetType());
foreach (object item in list) result.Add(item);
return result;
}
일반 목록에 적용 :
List<T> Clone<T>(List<T> argument) => (List<T>)CloneList(argument);
다음 코드는 최소한의 변경으로 목록으로 전송해야합니다.
기본적으로 각 연속 루프마다 더 큰 범위에서 새로운 난수를 삽입하여 작동합니다. 이미 동일하거나 높은 숫자가있는 경우, 임의의 숫자를 1만큼 위로 이동하여 더 큰 범위의 새로운 임의 인덱스로 전송하십시오.
// Example Usage
int[] indexes = getRandomUniqueIndexArray(selectFrom.Length, toSet.Length);
for(int i = 0; i < toSet.Length; i++)
toSet[i] = selectFrom[indexes[i]];
private int[] getRandomUniqueIndexArray(int length, int count)
{
if(count > length || count < 1 || length < 1)
return new int[0];
int[] toReturn = new int[count];
if(count == length)
{
for(int i = 0; i < toReturn.Length; i++) toReturn[i] = i;
return toReturn;
}
Random r = new Random();
int startPos = count - 1;
for(int i = startPos; i >= 0; i--)
{
int index = r.Next(length - i);
for(int j = startPos; j > i; j--)
if(toReturn[j] >= index)
toReturn[j]++;
toReturn[i] = index;
}
return toReturn;
}
또 다른 것은 : 당신은 반사를 사용할 수 있습니다. 이것을 올바르게 캐시하면 5.6 초 안에 1,000,000 개의 객체를 복제 할 수 있습니다 (내부 객체로는 16.4 초).
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Person
{
...
Job JobDescription
...
}
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Job
{...
}
private static readonly Type stringType = typeof (string);
public static class CopyFactory
{
static readonly Dictionary<Type, PropertyInfo[]> ProperyList = new Dictionary<Type, PropertyInfo[]>();
private static readonly MethodInfo CreateCopyReflectionMethod;
static CopyFactory()
{
CreateCopyReflectionMethod = typeof(CopyFactory).GetMethod("CreateCopyReflection", BindingFlags.Static | BindingFlags.Public);
}
public static T CreateCopyReflection<T>(T source) where T : new()
{
var copyInstance = new T();
var sourceType = typeof(T);
PropertyInfo[] propList;
if (ProperyList.ContainsKey(sourceType))
propList = ProperyList[sourceType];
else
{
propList = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
ProperyList.Add(sourceType, propList);
}
foreach (var prop in propList)
{
var value = prop.GetValue(source, null);
prop.SetValue(copyInstance,
value != null && prop.PropertyType.IsClass && prop.PropertyType != stringType ? CreateCopyReflectionMethod.MakeGenericMethod(prop.PropertyType).Invoke(null, new object[] { value }) : value, null);
}
return copyInstance;
}
Watcher 클래스를 사용하여 간단한 방법으로 측정했습니다.
var person = new Person
{
...
};
for (var i = 0; i < 1000000; i++)
{
personList.Add(person);
}
var watcher = new Stopwatch();
watcher.Start();
var copylist = personList.Select(CopyFactory.CreateCopyReflection).ToList();
watcher.Stop();
var elapsed = watcher.Elapsed;
결과 : PersonInstance 내부 개체-16.4, PersonInstance = null-5.6
CopyFactory는 표현식 사용을 포함하여 수십 가지 테스트가있는 테스트 클래스 일뿐입니다. 이것을 확장 또는 다른 형태로 다른 형태로 구현할 수 있습니다. 캐싱을 잊지 마십시오.
아직 직렬화를 테스트하지는 않았지만 백만 개의 클래스로 개선하는 데는 의문의 여지가 있습니다. 빠른 protobuf / newton을 시도하겠습니다.
추신 : 읽기 간단하게하기 위해 여기서는 자동 속성 만 사용했습니다. FieldInfo로 업데이트하거나 직접 구현할 수 있습니다.
최근 에 DeepClone 기능을 사용 하여 프로토콜 버퍼 직렬 변환기를 즉시 테스트했습니다 . 백만 개의 단순한 물체에서 4.2 초가 걸리지 만 내부 물체의 경우 7.4 초로 이깁니다.
Serializer.DeepClone(personList);
요약 : 수업에 액세스 할 수 없으면 도움이됩니다. 그렇지 않으면 개체의 수에 따라 다릅니다. 최대 10,000 개의 객체 (약간 작을 수도 있음)를 사용할 수 있다고 생각하지만 이보다 많은 경우 프로토콜 버퍼 직렬 변환기의 성능이 향상됩니다.
JSON 시리얼 라이저 및 디시리얼라이저를 사용하여 C #에서 객체를 복제하는 간단한 방법이 있습니다.
확장 클래스를 만들 수 있습니다 :
using Newtonsoft.Json;
static class typeExtensions
{
[Extension()]
public static T jsonCloneObject<T>(T source)
{
string json = JsonConvert.SerializeObject(source);
return JsonConvert.DeserializeObject<T>(json);
}
}
복제하고 반대하려면 :
obj clonedObj = originalObj.jsonCloneObject;
딥 카피의 경우 ICloneable이 올바른 솔루션이지만 ICloneable 인터페이스 대신 생성자를 사용하여 ICloneable에 대한 비슷한 접근 방식이 있습니다.
public class Student
{
public Student(Student student)
{
FirstName = student.FirstName;
LastName = student.LastName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
}
// wherever you have the list
List<Student> students;
// and then where you want to make a copy
List<Student> copy = students.Select(s => new Student(s)).ToList();
사본을 만들려면 다음 라이브러리가 필요합니다.
using System.Linq
System.Linq 대신 for 루프를 사용할 수도 있지만 Linq는 간결하고 깨끗합니다. 마찬가지로 다른 답변이 제안하고 확장 방법 등을 만들 수 있지만 그렇게 할 필요는 없습니다.
참고 URL : https://stackoverflow.com/questions/222598/how-do-i-clone-a-generic-list-in-c
'development' 카테고리의 다른 글
JavaScript에서 부동 소수점 숫자 정밀도를 처리하는 방법은 무엇입니까? (0) | 2020.02.10 |
---|---|
PHP 치명적 오류를 잡는 방법 (0) | 2020.02.10 |
Docker 컨테이너의 파일 시스템 탐색 (0) | 2020.02.10 |
다형성이란 무엇이며, 그 목적은 무엇이며 어떻게 사용됩니까? (0) | 2020.02.10 |
목록을 변환하는 방법 (0) | 2020.02.10 |