programing

C#에서 범용 목록을 복제하려면 어떻게 해야 하나요?

sourcejob 2023. 4. 14. 21:39
반응형

C#에서 범용 목록을 복제하려면 어떻게 해야 하나요?

C#에 오브젝트의 범용 리스트가 있어 그 리스트를 복제하고 싶다.은 복제할 수 , 복제는 할 수 있는 것 같습니다.list.Clone().

쉬운 방법이 없을까요?

요소가 값 유형인 경우 다음을 수행할 수 있습니다.

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모든 멤버들의 깊은 복사를 보장해야 하기 때문입니다.대신 나 대, 사, method, , method, method, method, method, method, method, method, method, method, method, method, method, method, method, method, method, method, method, method method, , methodYourType.CopyFrom(YourType itemToCopy)됩니다.YourType.

이러한 옵션은 메서드(확장 또는 기타)로 래핑할 수 있습니다.

확장 방법을 사용할 수 있습니다.

static class Extensions
{
    public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
    {
        return listToClone.Select(item => (T)item.Clone()).ToList();
    }
}

얕은 복사본의 경우 대신 일반 목록 클래스의 GetRange 메서드를 사용할 수 있습니다.

List<int> oldList = new List<int>( );
// Populate oldList...

List<int> newList = oldList.GetRange(0, oldList.Count);

인용처:범용 레시피

public static object DeepClone(object obj) 
{
    object objResult = null;

    using (var ms = new MemoryStream())
    {
        var bf = new BinaryFormatter();
        bf.Serialize(ms, obj);

        ms.Position = 0;
        objResult = bf.Deserialize(ms);
     }

     return objResult;
}

이것은 C# 및 를 사용하는 경우의1가지 방법입니다.NET 2.0 신신음음음음음음음음음음음음음음음음음 net로 해야 합니다.[Serializable()]목표는 모든 레퍼런스를 잃고 새로운 레퍼런스를 구축하는 것입니다.

목록을 복제하려면 을 클릭하십시오.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 }
> 

약간의 변경 후 다음 복제도 가능합니다.

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

는 제외합니다.List<T>목록을 복제하는 가장 좋은 방법은 오래된 목록을 수집 파라미터로 사용하여 새 목록을 작성하는 것입니다.

List<T> myList = ...;
List<T> cloneOfMyList = new List<T>(myList);

「 」로의 myList를 들어 또는 을 주지 .cloneOfMyList그리고 역도 성립.

그러나 두 목록에 포함된 실제 개체는 여전히 동일합니다.

AutoMapper(또는 원하는 매핑 lib)를 사용하여 복제는 단순하고 유지보수가 가능합니다.

매핑을 정의합니다.

Mapper.CreateMap<YourType, YourType>();

마법을 부려라:

YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);

값 유형만 신경 쓴다면...

그리고 그 종류도 알겠죠?

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

당신이 이미 뉴턴소프트를 참조했다면.프로젝트의 Json 및 객체는 언제든지 사용할 수 있습니다.

List<T> newList = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(listToCopy))

가장 효율적인 방법은 아닐 수도 있지만, 100/1000회 이상 하지 않으면 속도 차이를 알아차리지 못할 수도 있습니다.

상세한 카피에서는, 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

시스템 대신 for 루프를 사용할 수도 있습니다.린크, 하지만 린크는 간결하고 깔끔하게 표현해.마찬가지로 다른 답변에 따라 확장 방법 등을 만들 수 있지만, 이 중 어느 것도 필요하지 않습니다.

이 테스트에서는 클래스에 시리얼 가능 플래그를 붙일 필요가 없으며, 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);
}
 //try this
 List<string> ListCopy= new List<string>(OldList);
 //or try
 List<T> ListCopy=OldList.ToList();

누가 이걸 읽으면 난 운이 좋을 거야단, Clone 메서드에서 type 객체의 목록을 반환하지 않기 위해 인터페이스를 만들었습니다.

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

좋아하는 사람?개선점은?

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

같은 용량의 클론 리스트가 필요한 경우는, 다음과 같이 시험할 수 있습니다.

public static List<T> Clone<T>(this List<T> oldList)
{
    var newList = new List<T>(oldList.Capacity);
    newList.AddRange(oldList);
    return newList;
}

컬렉션의 상세한 복사가 필요한 경우, 다음과 같은 방법을 선호합니다.

public static IEnumerable<T> DeepCopy<T>(this IEnumerable<T> collectionToDeepCopy)
{
    var serializedCollection = JsonConvert.SerializeObject(collectionToDeepCopy);
    return JsonConvert.DeserializeObject<IEnumerable<T>>(serializedCollection);
}

확장 방식을 사용할 수 있습니다.

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

주의: 복사(또는 복제)를 변경해도 원래 개체에는 영향을 주지 않습니다.

이 경우 캐스트를 사용하면 다음과 같이 얕은 복사에 도움이 될 수 있습니다.

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

오브젝트를 복사하기 위해 오토마퍼를 사용합니다.하나의 객체를 매핑하는 매핑을 설정했을 뿐입니다.이 작업은 원하는 방식으로 래핑할 수 있습니다.

http://automapper.codeplex.com/

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

.ToArray를 사용하여 Array.Clone(...)필요에 따라 어레이 클래스에 포함된 메서드가 고객의 요구를 충족시킬 수 있습니다.

다음 코드는 최소한의 변경으로 목록으로 전송됩니다.

기본적으로는 연속되는 루프마다 더 큰 범위에서 새로운 난수를 삽입함으로써 동작합니다.동일하거나 그보다 높은 숫자가 이미 존재하는 경우 해당 난수들을 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;
}

또 다른 것은 반성을 할 수 있다는 것입니다.이 캐시를 올바르게 하면 1,000,000개의 개체가 5.6초 내에 복제됩니다(내부 개체의 경우 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;
    }

워처 클래스를 사용하여 간단하게 측정했습니다.

 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

Copy Factory는 표현식 사용을 포함한 12개의 테스트를 수행하는 테스트 클래스입니다.이것을 다른 형태로 확장 또는 다른 형태로 구현할 수 있습니다.캐싱도 잊지 마세요.

아직 연재 테스트를 해보진 않았지만, 100만 개의 클래스가 있는 개선은 의심스럽네요.빠른 프로토부프/뉴턴으로 하겠습니다.

추신: 간결하게 읽기 위해서, 저는 자동차 소유물만을 사용했습니다.FieldInfo로 업데이트 할 수도 있고 직접 쉽게 구현할 수도 있습니다.

최근에 DeepClone 기능을 바로 사용할 수 있는 Protocol Buffers 시리얼라이저를 테스트했습니다.단순 객체 100만 개에서는 4.2초로 이기지만 내부 객체에서는 7.4초로 이긴다.

Serializer.DeepClone(personList);

요약: 클래스에 액세스할 수 없는 경우 도움이 됩니다.그렇지 않으면 개체 수에 따라 달라집니다.리플렉션을 사용할 수 있는 객체는 최대 10,000개(약간 적을 수 있음)이지만, 이 이상의 경우 Protocol Buffers serializer가 더 잘 작동합니다.

JSON serializer 및 deserializer를 사용하여 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;

딥 클론의 경우 다음과 같이 반사를 사용합니다.

public List<T> CloneList<T>(IEnumerable<T> listToClone) {
    Type listType = listToClone.GetType();
    Type elementType = listType.GetGenericArguments()[0];
    List<T> listCopy = new List<T>();
    foreach (T item in listToClone) {
        object itemCopy = Activator.CreateInstance(elementType);
        foreach (PropertyInfo property in elementType.GetProperties()) {
            elementType.GetProperty(property.Name).SetValue(itemCopy, property.GetValue(item));
        }
        listCopy.Add((T)itemCopy);
    }
    return listCopy;
}

List 또는 IEnumberable을 자유롭게 사용할 수 있습니다.

를 사용할 수 있습니다.List<T>.ConvertAll(Converter<T, T>)method는 원래 목록의 모든 요소를 포함하는 새 목록을 만들고 입력 값을 반환하는 변환 함수를 사용합니다.

List<int> originalList = new List<int> { 1, 2, 3, 4, 5 };
List<int> clonedList = new List<int>(originalList.ConvertAll(x => x));

언급URL : https://stackoverflow.com/questions/222598/how-do-i-clone-a-generic-list-in-c

반응형