【问题标题】:Preserve value of an object instance保留对象实例的值
【发布时间】:2015-04-21 09:39:47
【问题描述】:

我认为这个问题是 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();
        }      

【问题讨论】:

  • 为什么不把列表分配给保留列表???
  • 这取决于searchList() 返回的内容。如果它总是返回新的对象,你可以照你写的去做。如果它总是返回相同的对象,那么不,你不能那么简单地做到这一点。它完全是特定于实现的。
  • 恰如其分:一个对象是一个类的一个实例。
  • 请参阅"Should questions include “tags” in their titles?",其中的共识是“不,他们不应该”!
  • 好吧,如果你不能使用任何深拷贝方法,那么你唯一的希望就是 CSpropr.searchList 每次调用时都会返回一个新的 CSpropr 列表。

标签: c# preserve


【解决方案1】:

在这种情况下,我会根据这个答案使用 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

【讨论】:

  • 这对于简单的对象来说可能有点过头了。如果该对象只包含一些简单的属性,我认为向它添加一个 Clone 方法并执行类似OriginalList.ForEach(f =&gt; ClonedList.Add(f.Clone())); 之类的操作会更快,还有the answers here might help as well
  • 谢谢,但由于其他原因,我认为我无法使 CSpropr 可克隆或可序列化。
  • 好吧,如果 serealizing 不是一个选项,我会使用@ZoharPeled 的建议,或者使用 AutoMapper。这取决于你的程序是否必须是可扩展的等等......
【解决方案2】:

您所说的与 c# 对象中的 transactions 接近。 c# 中没有对对象事务的固有支持。

Microsoft 确实在 STM(软件事务内存)上启动了一个项目,以支持 .NET 对象的 transaction 功能。但是,由于各种原因,该项目已经退役。

因此,您现在所能做的就是为原始列表创建一个单独的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();

示例取自互联网上优秀的blogs之一,用于c#

当然,当我指的是对象时,它也适用于List&lt;objects&gt;

【讨论】:

  • 感谢@ANewGuyInTown,但我无法将我的 CSpropr 类更改为可克隆。
  • 好吧,在这种情况下,如果您没有查看 Deep Cloning 的选项,您可以为您的实现 IClonable 的类创建一个包装类,我会为您提供一个示例,如果你有兴趣。
  • See MemberwiseClone documentation: "MemberwiseClone 方法通过创建一个新对象来创建浅拷贝,然后将当前对象的非静态字段复制到新对象中。如果字段是值类型,则对字段进行逐位复制。如果字段是引用类型,复制了引用,但不复制引用的对象;因此,原始对象及其克隆引用相同对象。”
  • @ZoharPeled 是的,你是对的,因此 Person 类中的对象的单独成员明智克隆是 Address。如果仔细观察,类中的所有字段都是value types,任何其他引用类型都将实现它自己的 IClonable 接口。
  • @ANewGuyInTown 我看到你做得对。我只是认为为了清楚起见值得一提。
【解决方案3】:

基本上你想要两个相同的列表,但如果你改变 list1 的一个元素,它不应该影响 list2 的同一个元素。 您可以通过多种方式执行此操作:例如使用序列化。

以下代码将显示我的方式:

模块模块1

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
更多

因此,无论原始列表如何更改,克隆的列表都不会受到影响

【讨论】:

    【解决方案4】:

    你可以像这样存储你的第一个列表对象:

    List<CSpropr> StoreFirstListInTemp(List<CSpropr> listActionpropr)
    {
      List<CSpropr> temp = new List<CSpropr>();
      temp.AddRange(listActionpropr);
      return temp;
    }
    

    【讨论】:

    • 投反对票原因:您的答案是错误的。您的临时列表将简单地为原始列表中的每个 SCpropr 对象保存另一个引用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-08
    • 1970-01-01
    • 1970-01-01
    • 2019-10-21
    • 1970-01-01
    • 1970-01-01
    • 2014-12-19
    相关资源
    最近更新 更多