web-dev-qa-db-fra.com

Copie détaillée de la liste <T>

J'essaie de créer une copie complète d'une liste générique et je me demande s'il existe un autre moyen de créer une méthode de copie et de copier chaque membre un à la fois. J'ai un cours qui ressemble un peu à ça:

public class Data
{            
    private string comment;
    public string Comment
    {
        get { return comment; }
        set { comment = value; }
    }

    private List<double> traceData;
    public List<double> TraceData
    {
        get { return traceData; }
        set { traceData = value; }
    }
}

Et j'ai une liste des données ci-dessus, c'est-à-dire List<Data>. Ce que j'essaie de faire, c'est de tracer les données de trace du sous-ensemble de la liste sur un graphique, éventuellement avec une mise à l'échelle ou un balayage des données. Je n'ai évidemment pas besoin de tout représenter dans la liste car ils ne rentrent pas dans l'écran. 

J'ai d'abord essayé d'obtenir le sous-ensemble de la liste en utilisant la méthode List.GetRange(), mais il semble que le List<double> situé en dessous soit en cours de copie superficielle au lieu de copie en profondeur. Lorsque je récupère le sous-ensemble à l'aide de List.GetRange (), j'obtiens des données précédemment modifiées, pas les données brutes extraites ailleurs.

Quelqu'un peut-il me donner une direction sur la façon d'aborder cela? Merci beaucoup.

15
thomas1234

La façon idiomatique d’aborder ceci en C # consiste à implémenter ICloneable sur votre Data et à écrire une méthode Clone qui effectue la copie complète (puis vraisemblablement une méthode Enumerable.CloneRange qui peut cloner une partie de votre liste à la fois.) N’importe quelle astuce ou méthode intégrée permettant de simplifier les choses.

À moins que la mémoire et les performances ne soient réellement une préoccupation, je vous suggère cependant de tout mettre en œuvre pour le reconfigurer afin qu'il fonctionne sur des objets Data immuables, à la place. Ça va finir beaucoup plus simple.

10
mquander

Tu peux essayer ça

    public static object DeepCopy(object obj)
    {
        if (obj == null)
            return null;
        Type type = obj.GetType();

        if (type.IsValueType || type == typeof(string))
        {
            return obj;
        }
        else if (type.IsArray)
        {
            Type elementType = Type.GetType(
                 type.FullName.Replace("[]", string.Empty));
            var array = obj as Array;
            Array copied = Array.CreateInstance(elementType, array.Length);
            for (int i = 0; i < array.Length; i++)
            {
                copied.SetValue(DeepCopy(array.GetValue(i)), i);
            }
            return Convert.ChangeType(copied, obj.GetType());
        }
        else if (type.IsClass)
        {

            object toret = Activator.CreateInstance(obj.GetType());
            FieldInfo[] fields = type.GetFields(BindingFlags.Public |
                        BindingFlags.NonPublic | BindingFlags.Instance);
            foreach (FieldInfo field in fields)
            {
                object fieldValue = field.GetValue(obj);
                if (fieldValue == null)
                    continue;
                field.SetValue(toret, DeepCopy(fieldValue));
            }
            return toret;
        }
        else
            throw new ArgumentException("Unknown type");
    }

Merci à DetoX83 article sur le projet de code.

Si le mode IClonable est trop difficile pour vous. Je suggère de convertir en quelque chose et retour. Cela peut être fait avec BinaryFormatter ou un convertisseur Json comme Servicestack.Text puisqu'il s'agit du plus rapide en .Net.

Le code devrait être quelque chose comme ceci:

MyClass mc = new MyClass();
string json = mc.ToJson();
MyClass mcCloned = json.FromJson<MyClass>();

mcCloned ne fera pas référence à mc.

5
Xelom

La manière la plus simple (mais sale) consiste à implémenter ICloneable par votre classe et à utiliser la méthode d'extension suivante:

public static IEnumerable<T> Clone<T>(this IEnumerable<T> collection) where T : ICloneable
{
    return collection.Select(item => (T)item.Clone());
}

Usage: 

var list = new List<Data> { new Data { Comment = "comment", TraceData = new List { 1, 2, 3 } };
var newList = list.Clone();
3
abatishchev

vous pouvez également marquer votre classe avec la variable serializable et utiliser la sérialisation binaire. Voici un exemple de travail.

   public class Program
    {
        [Serializable]
        public class Test
        {
            public int Id { get; set; }
            public Test()
            {

            }
        }

        public static void Main()
        {   
            //create a list of 10 Test objects with Id's 0-10
            List<Test> firstList = Enumerable.Range(0,10).Select( x => new Test { Id = x } ).ToList();
            using (var stream = new System.IO.MemoryStream())

            {
                 var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                 binaryFormatter.Serialize(stream, firstList); //serialize to stream
                 stream.Position = 0;
                 //deserialize from stream.
                 List<Test> secondList = binaryFormatter.Deserialize(stream) as List<Test>; 
            }


            Console.ReadKey();
        }
    }
1
Stan R.

Si vous rendez vos objets immuables, vous n'avez pas à vous soucier de les transmettre, vous pouvez faire quelque chose comme:

var toPlot = list.Where(d => d.ShouldBePlotted());
0
jonnii

Un moyen quick et generic de sérialiser en profondeur un objet consiste à utiliser JSON.net . La méthode d'extension suivante permet de sérialiser une liste d'objets quelconques, mais permet d'ignorer Entity Framework. propriétés de navigation, car elles peuvent entraîner des dépendances circulaires et des extractions de données indésirables.

Méthode

public static List<T> DeepClone<T>(this IList<T> list, bool ignoreVirtualProps = false)
{
    JsonSerializerSettings settings = new JsonSerializerSettings();
    if (ignoreVirtualProps)
    {
        settings.ContractResolver = new IgnoreNavigationPropsResolver();
        settings.PreserveReferencesHandling = PreserveReferencesHandling.None;
        settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
        settings.Formatting = Formatting.Indented;
    }

    var serialized = JsonConvert.SerializeObject(list, settings);
    return JsonConvert.DeserializeObject<List<T>>(serialized);
}

Utilisation

var clonedList = list.DeepClone();

Par défaut, JSON.NET ne sérialise que les propriétés publiques. Si les propriétés privées doivent également être clonées, cette solution peut être utilisée.

Cette méthode permet de dés) (rapide) sérialisation de hiérarchies complexes d'objets .

0
Alexei

Comme votre collection est modifiable, vous devez implémenter la copie profonde par programme:

public class Data
{
    public string Comment { get; set; }
    public List<double> TraceData { get; set; }

    public Data DeepCopy()
    {
        return new Data
        {
            Comment = this.Comment, 
            TraceData = this.TraceData != null
                ? new List<double>(this.TraceData)
                : null;
        }
    }
}

Le champ Comment peut être copié de manière superficielle car il s'agit déjà d'une classe immuable. Vous devez créer une nouvelle liste pour TraceData, mais les éléments eux-mêmes sont immuables et ne nécessitent aucune manipulation particulière pour les copier.

Lorsque je récupère le sous-ensemble en utilisant List.GetRange (), j’obtiens précédemment des données modifiées , Et non des données brutes Extraites ailleurs.

Utilisez votre nouvelle méthode DeepCopy en tant que telle:

var pointsInRange = dataPoints
    .Select(x => x.DeepCopy())
    .GetRange(start, length);
0
Juliet
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DeepListCopy_testingSome
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> list1 = new List<int>();
            List<int> list2 = new List<int>();

            //populate list1
            for (int i = 0; i < 20; i++)
            {
                list1.Add(1);
            }

            ///////
            Console.WriteLine("\n int in each list1 element is:\n");
            ///////

            foreach (int i in list1)
            {
                Console.WriteLine(" list1 elements: {0}", i);
                list2.Add(1);
            }

            ///////
            Console.WriteLine("\n int in each list2 element is:\n");
            ///////

            foreach (int i in list2)
            {
                Console.WriteLine(" list2 elements: {0}", i);
            }

            ///////enter code here

            for (int i = 0; i < list2.Count; i++)
            {
                list2[i] = 2;
            }



            ///////
            Console.WriteLine("\n Printing list1 and list2 respectively to show\n"
                            + " there is two independent lists,i e, two differens"
                            + "\n memory locations after modifying list2\n\n");
            foreach (int i in list1)
            {
                Console.WriteLine(" Printing list1 elements: {0}", i);
            }

            ///////
            Console.WriteLine("\n\n");
            ///////

            foreach (int i in list2)
            {
                Console.WriteLine(" Printing list2 elements: {0}", i);
            }

            Console.ReadKey();
        }//end of Static void Main
    }//end of class
}
0
user7433127