Сохранить значение экземпляра объекта

Я думаю, что эта проблема связана с проблемой создания экземпляра значения / ссылки в объектно-ориентированных языках, таких как C #. Но я новичок и не знаю, как исправить ситуацию.

У меня есть метод с этим фрагментом кода:

    List<CSpropr> listActionpropr = CSpropr.searchList(actionCondition); // Get a list of all records of table PROPR for the 'actioncondition' specified
// For each record...
    foreach (CSpropr instance in listActionpropr)
       {
         instance.ValName = "John";
         instance.ValPhone = 323234232;
         instance.update(); // This make and UPDATE of the record in DB
       }

Позже в том же методе я хочу использовать первую версию listActionpropr перед обновлением. Сделать этакий откат. Но когда я повторяю переменную listActionpropr, я получаю список записей с изменениями в значениях Name и Phone. Например:

foreach (CSApropr instance1 in listActionpropr )
    {
      instance1.update();
    }

Есть ли элегантный способ сохранить значение без создания нового поиска для другой переменной? Нравится:

List<CSpropr> listActionpropr = CSpropr.searchList(actionCondition); // Get a list of all records of table PROPR for the 'actioncondition' specified
List<CSpropr> preservedList = CSpropr.searchList(actionCondition); // Get a list of all records of table PROPR for the 'actioncondition' specified
    // For each record...
        foreach (CSpropr instance in listActionpropr)
           {
             instance.ValName = "John";
             instance.ValPhone = 323234232;
             instance.update(); // This make and UPDATE of the record in DB
           }
....

foreach (CSApropr instance1 in preservedList )
        {
          instance1.update();
        }      

person milheiros    schedule 21.04.2015    source источник
comment
Почему бы просто не присвоить список сохраненному списку ???   -  person Coder1409    schedule 21.04.2015
comment
Это зависит от того, что возвращает searchList(). Если он всегда возвращает новые объекты, вы можете делать то, что написали. Если он всегда возвращает одни и те же объекты, то нет, вы не можете сделать это так просто. Это полностью зависит от реализации.   -  person Sami Kuhmonen    schedule 21.04.2015
comment
По поводу вашего заголовка: объект - это экземпляр класса.   -  person Rob    schedule 21.04.2015
comment
См. Раздел Должны ли вопросы включать «теги» в свои заголовки? , где нет единого мнения, не должны!   -  person    schedule 21.04.2015
comment
Что ж, если вы не можете использовать какой-либо метод глубокого копирования, тогда ваша единственная надежда состоит в том, что CSpropr.searchList будет возвращать список новых CSpropr каждый раз, когда он вызывается.   -  person Zohar Peled    schedule 21.04.2015


Ответы (4)


В этом случае я бы использовал DeepClone с сериализацией согласно этому ответу: https://stackoverflow.com/a/519512/841467

Моя быстрая реализация в LinqPad была:

void Main()
{
    List<CSpropr> listActionpropr = CSpropr.searchList("act"); // Get a list of all records of table PROPR for the 'actioncondition' specified
    List<CSpropr> preservedList = listActionpropr.DeepCopy(); // Get a list of all records of table PROPR for the 'actioncondition' specified
    // For each record...
    foreach (CSpropr instance in listActionpropr)
    {
        instance.ValName = "John";
        instance.ValPhone = 323234232;
        instance.update(); // This make and UPDATE of the record in DB
    }

    foreach (CSpropr instance1 in preservedList )
    {
        instance1.update();
    }
}

// Define other methods and classes here
[Serializable]
public class CSpropr {
    public string ValName {get;set;}
    public int ValPhone {get;set;}

    public void update() {
        ValName.Dump();
        ValPhone.Dump();
    }

    public static List<CSpropr> searchList(string act) {
        return new List<CSpropr> { new CSpropr {ValName = "First", ValPhone = 4444} , new CSpropr {ValName = "First", ValPhone = 4444 }};   
    }
}

public static class GenericCopier
{
    public static T DeepCopy<T>(this T original) where T : class
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            binaryFormatter.Serialize(memoryStream, original);
            memoryStream.Seek(0, SeekOrigin.Begin);
            return (T)binaryFormatter.Deserialize(memoryStream);
        }
    }
}

результат:

John 323234232 John 323234232 First 4444 First 4444

person Igoris Azanovas    schedule 21.04.2015
comment
Для простых предметов это может быть излишним. если объект содержит только несколько простых свойств, я думаю, будет быстрее добавить к нему метод Clone и сделать что-то вроде OriginalList.ForEach(f => ClonedList.Add(f.Clone())); Кроме того, ответы здесь тоже могут помочь. - person Zohar Peled; 21.04.2015
comment
Спасибо, но я не могу сделать CSpropr Cloneable или Serializable по другим причинам. - person milheiros; 21.04.2015
comment
Хорошо, если serealizing не вариант, я бы использовал предложение @ZoharPeled или использовал AutoMapper. Это зависит от того, должна ли ваша программа быть расширяемой и так далее ... - person Igoris Azanovas; 21.04.2015

То, о чем вы говорите, близко к transactions в объектах C #. В C # нет встроенной поддержки объектных транзакций.

Microsoft начала проект на STM (программная транзакционная память) для поддержки transaction функций для объектов .NET. Однако проект уже закрыт по разным причинам.

Итак, все, что вы можете сделать сейчас, это создать отдельный object для исходного списка. Вы можете либо DeepCopy объект с помощью некоторого вспомогательного метода, как описано в другом ответе, либо реализовать IClonable интерфейс для вашего объекта, который делает глубокую копию для вас. Примером может быть:

public class Person : ICloneable
{
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public Address PersonAddress { get; set; }

    public object Clone()
    {
        Person newPerson = (Person)this.MemberwiseClone();
        newPerson.PersonAddress = (Address)this.PersonAddress.Clone();

        return newPerson;
    }
}

Класс Address использует MemberwiseClone для создания своей копии.

public class Address : ICloneable
{
    public int HouseNumber { get; set; }
    public string StreetName { get; set; }

    public object Clone()
    {
        return this.MemberwiseClone();
    }
}

Клонирование человека:

Person herClone = (Person)emilyBronte.Clone();

Пример взят из одного из отличных блоги в Интернете для C #

Конечно, когда я имел в виду объект, это применимо и к List<objects>.

person ANewGuyInTown    schedule 21.04.2015
comment
Спасибо @ANewGuyInTown, но я не могу изменить свой класс CSpropr на Cloneable. - person milheiros; 21.04.2015
comment
Что ж, в этом случае, если вы не рассматриваете вариант Deep Cloning, вы можете создать класс-оболочку для своего класса, который реализует IClonable, я предоставлю вам пример, если вам интересно. - person ANewGuyInTown; 21.04.2015
comment
См. документацию MemberwiseClone: метод MemberwiseClone создает неглубокий скопировать, создав новый объект, а затем скопировав нестатические поля текущего объекта в новый объект. Если поле имеет тип значения, выполняется побитовое копирование поля. Если поле является ссылочным типом, ссылка копируется, а упомянутый объект - нет; поэтому исходный объект и его клон относятся к одному и тому же объекту. - person Zohar Peled; 21.04.2015
comment
@ZoharPeled Да, вы правы, отсюда отдельный поэлементный клон для объекта внутри класса Person, то есть Address. Если вы присмотритесь, все поля внутри класса value types, а для любых других ссылочных типов будет реализован собственный интерфейс IClonable. - person ANewGuyInTown; 21.04.2015
comment
@ANewGuyInTown Я видел, что вы все сделали правильно. Я просто подумал, что об этом стоит упомянуть для ясности. - person Zohar Peled; 21.04.2015
comment
@ZoharPeled, не беспокойтесь. - person ANewGuyInTown; 21.04.2015

В основном вам нужны два идентичных списка, но если вы измените один элемент списка1, это не должно повлиять на тот же элемент списка2. Вы можете сделать это разными способами: например, используя сериализацию.

Следующий код покажет мой путь:

Модуль Module1

Sub Main()

    Dim listone As New List(Of demo) 'first list
    Dim listwo As New List(Of demo)   ' second list
    Dim var1 As Integer
    Dim var2 As String
    Dim obj As New demo()           'first object created in list-1
    obj.id = 1
    obj.name = "sumi"
    listone.Add(obj)                'first object inserted in the list-1
    Dim obj1 As New demo()
    obj1.id = 3
    obj1.name = "more"
    listone.Add(obj1)               'Second object inserted in the list-1
    For Each w In listone
        Dim obj3 As New demo()
        var1 = w.id
        obj3.id = var1
        var2 = w.name
        obj3.name = var2
        listwo.Add(obj3)            'looping all objects of list-1 and adding them in list-2 .Hence making both lists identical

    Next
    For Each p In listone      'making change in the list-1 and this change should not be refelected in list-2
        If (p.id = 1) Then
            p.id = 5
        End If
    Next
    For Each z In listone
        Console.WriteLine(z.id)
        Console.WriteLine(z.name)

    Next
    For Each q In listwo
        Console.WriteLine(q.id)
        Console.WriteLine(q.name)
    Next
    Console.ReadLine()

End Sub
Class demo
    Public name As String
    Public id As Integer
End Class

Конечный модуль

Вывод:
5
суми
3
еще
1
суми
3
еще

Следовательно, клонированный список остается неизменным независимо от изменений в исходном списке.

person sumeet more    schedule 24.09.2017

Вы можете сохранить свой первый объект списка следующим образом:

List<CSpropr> StoreFirstListInTemp(List<CSpropr> listActionpropr)
{
  List<CSpropr> temp = new List<CSpropr>();
  temp.AddRange(listActionpropr);
  return temp;
}
person Orkun Bekar    schedule 21.04.2015
comment
Причина голосования против: ваш ответ неверен. ваш временный список просто будет содержать еще одну ссылку для каждого объекта SCpropr в исходном списке. - person Zohar Peled; 21.04.2015