【问题标题】:C# How to remove reference between object when adding to List?C#添加到List时如何删除对象之间的引用?
【发布时间】:2015-08-13 03:07:09
【问题描述】:

我有一个复杂的对象,它有很多属性,包括字符串、小数和 ObservableColection、列表...

我想这样做:

foreach (ITransactionItem transactionItem in Transaction.TransactionItemCollection.ToList())
                {                    
                    transactionItem.PickUpQuantity = 0;
                    transactionItem.IsPickUp = false;
                    this.PickUpItemCollection.Add(transactionItem);
                }

问题是,当我更改 PickUpItemCollection 中的项目时,TransactionItemCollection 中的参考项目也更改了,我不想要它。

我尝试使用此 ExtensionMethod 从对象复制对象:

public static T CopyFrom<T>(this T toObject, object fromObject)
    {
        var fromObjectType = fromObject.GetType();

        foreach (PropertyInfo toProperty in toObject.GetType().GetProperties())
        {
            PropertyInfo fromProperty = fromObjectType.GetProperty(toProperty.Name);

            if (fromProperty != null) // match found
            {
                // check types
                var fromType = Nullable.GetUnderlyingType(fromProperty.PropertyType) ?? fromProperty.PropertyType;
                var toType = Nullable.GetUnderlyingType(toProperty.PropertyType) ?? toProperty.PropertyType;

                if (toType.IsAssignableFrom(fromType)&& toProperty.CanWrite)
                {
                    toProperty.SetValue(toObject, fromProperty.GetValue(fromObject, null), null);
                }
            }
        }

        return toObject;
    }

并在此代码块中实现为:

foreach (ITransactionItem transactionItem in Transaction.TransactionItemCollection.AsEnumerable().ToList())
                {
                    ITransactionItem transactionItemModel = new TransactionItemModel();
                    transactionItemModel.CopyFrom(transactionItem);

                    transactionItem.PickUpQuantity = 0;
                    transactionItem.IsPickUp = false;
                    this.PickUpItemCollection.Add(transactionItemModel);
                }

但问题仍然存在,我知道我可以将 transactionItemModel 的属性一一设置为 transactionItem 的属性,但是有很多属性,它也有很多 List/ObservableCollection。我认为这会是一个更好的主意,对吧?

请帮帮我。

谢谢

【问题讨论】:

  • CopyFrom 将返回一个新对象,它不会修改现有对象。
  • CopyFrom 不是递归的;如果PickUpItemCollection 上的属性之一是一个对象,并且您在CopyFrom 创建的实例上更改了该对象,则原始属性值也会更改,因为PickUpItemCollection 的两个实例将引用同一个对象。
  • 您可能需要编写一个新函数来代替将整数、字符串等设置为相同的值,并创建具有复制值的任何类对象的新实例
  • 考虑使用BinaryFormatter 或Json.NET 进行克隆,就像对这个问题Deep cloning objects 的第一个答案一样。
  • 更多选项,请看这里:Faster deep cloning

标签: c# object reference


【解决方案1】:

由于您的源和目标不一定属于同一类型(分别为ITransactionItemTransactionItemModel)并且您的目标对象是预先分配的,您可以(递归地)deep-copy 源中的所有公共属性和字段通过序列化为 JSON 到目标,然后使用 Json.NETPopulateObject 方法将它们填充到目标中:

public static class ObjectExtensions
{
    public static void CopyFrom<TTo, TFrom>(this TTo target, TFrom source) where TTo : class
    {
        if (target == null)
            throw new ArgumentNullException();
        // Preserve object graph structure and handle polymorphic fields.
        var settings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All, ReferenceLoopHandling = ReferenceLoopHandling.Serialize, TypeNameHandling = TypeNameHandling.Auto };
        var json = JsonConvert.SerializeObject(source, settings);
        JsonConvert.PopulateObject(json, target, settings);
    }
}

大多数其他序列化程序不支持填充现有对象,这就是我建议使用这种特定方法的原因。在这种情况下,使用BinaryFormatter 进行深度复制是一个糟糕的选择,因为它会反序列化为与序列化类型完全相同类型的新实例。

【讨论】:

    【解决方案2】:

    在不知道您的对象的情况下,一般的答案是 - 您需要对您的对象进行深层复制。如果对象很复杂,这并不像表面上看起来那么容易。 见StackOverflow - cloning objects

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-16
      • 2017-06-13
      • 2012-10-03
      • 1970-01-01
      • 2020-02-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多