替换堆上的对象

本文关键字:对象 替换 | 更新日期: 2023-09-27 18:22:37

也许这真的很简单,或者违反了所有规则,或者我只是不知道它叫什么,所以我找不到它。

无论如何,我希望能够替换堆上的整个对象。我添加了一个小的代码示例来展示我想做什么以及实现它的方法,但我只想知道是否有更优雅的方法?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BasicObjectTest
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Test> testList = new List<Test>
            {
                new Test {Value=1,NiceString="First" },
                new Test {Value=2,NiceString="Second" },
                new Test {Value=3,NiceString="Third" }
            };
            var replacementTestClass = new Test { Value = 2, NiceString = "NEW" };
            EasyWay(testList, replacementTestClass);
            var correctTestClass = testList.FirstOrDefault(x => x.Value == 2);
            Console.WriteLine(correctTestClass.NiceString); //Expecting "Forth"
            Console.ReadLine();
            HardWay(testList, replacementTestClass);
            correctTestClass = testList.FirstOrDefault(x => x.Value == 2);
            Console.WriteLine(correctTestClass.NiceString);
            Console.ReadLine();
        }
        private static void HardWay(List<Test> testList, Test replacementTestClass)
        {
            //This will work!
            var secondTestClass = testList.FirstOrDefault(x => x.Value == 2);
            CopyPropertiesUsingPropertyInfo(secondTestClass, replacementTestClass);

        }
        private static void CopyPropertiesUsingPropertyInfo(Test secondTestClass, Test replacementTestClass)
        {
            foreach(var pi in secondTestClass.GetType().GetProperties())
            {
                pi.SetValue(secondTestClass, pi.GetValue(replacementTestClass, null));
            }

        }
        private static void EasyWay(List<Test> testList, Test replacementTestClass)
        {
            //This wont work, but I want it to!
            var secondTestClass = testList.FirstOrDefault(x => x.Value == 2);
            secondTestClass = replacementTestClass;

        }
    }
}

和我的测试对象

class Test
{
    public int Value { get; set; }
    public string NiceString { get; set; }
}

必须有一种更优雅的方式来做到这一点?我知道为什么第一个选项不起作用:我只是更改了该变量的对象引用。

更新:利用这种想法,我理解了很长一段时间,现在我测试了这个,认为它会起作用,但测试失败了。为什么?我不是替换了这个对象,这样每个使用它的对象都应该使用新对象吗?参见下方的完整代码

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        var main = new Main { Property = 1 };
        var dependent = new Dependent(main);
        void ChangeRef(ref Main Oldmain, Main newMain)
        {
            Oldmain = newMain;
        }
        ChangeRef(ref main, new Main { Property = 5 });
        Assert.AreEqual(5,dependent.Main.Property);
    }
}
public class Main
{
    public int Property { get; set; }

}
public class Dependent
{
    public Dependent(Main main)
    {
        Main = main;
    }
    public Main Main { get; set; }
}

替换堆上的对象

必须有一种更优雅的方式来做到这一点?

你缺少一件基本的东西。当您在列表中搜索对象并找到一个对象时,您会返回指向该对象的引用的副本。这意味着,当你修改它时,你只是在修改副本。列表中的原始引用仍然指向同一个旧对象实例。

但是如果我没有一份清单呢。我刚刚在变量

然后,您可以使用ref关键字通过引用传递引用类型

public static void Main(string[] args)
{
    var test = new Test { Value = 1, NiceString = "First" };
    var newTest = new Test { Value = 2, NiceString = "AlteredTest!" };
    UpdateTest(ref test, newTest);
    Console.WriteLine(test.NiceString); // "AlteredTest!"
}
public static void UpdateTest(ref Test originalTest, Test other)
{
    originalTest = other;
}

解决这一问题的另一种方法是使用众所周知的"额外级别的间接"。

不是将对象存储在列表中,而是存储包装器对象。包装器对象提供了一个指向实际对象的"Item"字段。然后,您可以更新"项目"字段,将其指向新对象。

一个简单的通用包装类可以是这样的:

class Wrapper<T>
{
    public T Item;
    public Wrapper(T item)
    {
        Item = item;
    }
    public static implicit operator Wrapper<T>(T item)
    {
        return new Wrapper<T>(item);    
    }
}

然后你可以这样使用它:

using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication2
{
    class Test
    {
        public int Value { get; set; }
        public string NiceString { get; set; }
    }
    class Wrapper<T>
    {
        public T Item;
        public Wrapper(T item)
        {
            Item = item;
        }
        public static implicit operator Wrapper<T>(T item)
        {
            return new Wrapper<T>(item);    
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var testList = new List<Wrapper<Test>>
            {
                new Test {Value = 1, NiceString = "First"},
                new Test {Value = 2, NiceString = "Second"},
                new Test {Value = 3, NiceString = "Third"}
            };
            var replacementTestClass = new Test { Value = 2, NiceString = "NEW" };
            EasyWay(testList, replacementTestClass);
            var correctTestClass = testList.FirstOrDefault(x => x.Item.Value == 2);
            Console.WriteLine(correctTestClass.Item.NiceString); //Expecting "New"
            Console.ReadLine();
        }
        private static void EasyWay(List<Wrapper<Test>> testList, Test replacementTestClass)
        {
            var secondTestClass = testList.FirstOrDefault(x => x.Item.Value == 2);
            secondTestClass.Item = replacementTestClass;
        }
    }
}