替换堆上的对象
本文关键字:对象 替换 | 更新日期: 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;
}
}
}