You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PropertyGrid撤销功能异常:栈中对象随修改同步变化原因咨询

为什么栈中的对象会和PropertyGrid的修改同步?

嘿,这个问题的核心原因其实是C#中引用类型的工作机制,我来给你讲明白:

你定义的Employee是一个引用类型,当你把propertyGridObject推入栈_stack.Push(propertyGridObject)时,栈里存储的并不是这个对象本身,而是指向内存中该对象的引用地址。也就是说,栈里的“对象”和PropertyGrid绑定的propertyGridObject其实指向的是同一个内存实例。所以当你在PropertyGrid里修改属性时,本质上是在修改这个内存实例的内容,栈里的引用指向的还是同一个实例,自然会同步变化。

解决方案:保存对象的独立副本

要实现撤销功能,你需要把对象的副本推入栈,而不是原对象的引用。这里有几种常见的实现方式:

1. 手动实现克隆方法(推荐,性能可控)

在Employee类中添加一个Clone方法,创建一个新的Employee实例并复制所有属性:

public class Employee {
    [Browsable(false)]
    public int employeeKey { get; set; }
    [DisplayName("Name")]
    [Description("The name of the employee.")]
    public string employeeName { get; set; }
    [DisplayName("Active")]
    [Description("Indicates whether the employee is currently employed.")]
    public bool employeeIsActive { get; set; }

    // 手动实现克隆,创建完全独立的对象副本
    public Employee Clone() {
        return new Employee {
            employeeKey = this.employeeKey,
            employeeName = this.employeeName,
            employeeIsActive = this.employeeIsActive
        };
    }
}

然后修改frmProperties_Load方法,推入克隆后的对象:

private void frmProperties_Load(object sender, EventArgs e) {
    _stack = new Stack<object>();
    // 推入对象的副本,而非原引用
    if (propertyGridObject is Employee emp) {
        _stack.Push(emp.Clone());
    } else {
        // 针对其他类型可以扩展拷贝逻辑,或直接推入原对象(如果是值类型)
        _stack.Push(propertyGridObject);
    }
    propertyGrid.SelectedObject = propertyGridObject;
}

2. 序列化反序列化实现深拷贝

如果你的类结构比较复杂,手动克隆太麻烦,可以用序列化的方式实现深拷贝(比如System.Text.Json):

using System.Text.Json;

private void frmProperties_Load(object sender, EventArgs e) {
    _stack = new Stack<object>();
    // 通过序列化->反序列化生成独立副本
    var jsonString = JsonSerializer.Serialize(propertyGridObject);
    var clonedObject = JsonSerializer.Deserialize(jsonString, propertyGridObject.GetType());
    _stack.Push(clonedObject);
    propertyGrid.SelectedObject = propertyGridObject;
}

3. 使用MemberwiseClone(浅拷贝)

如果Employee的所有属性都是值类型(比如int、bool)或不可变引用类型(比如string),可以用MemberwiseClone实现浅拷贝:

public class Employee {
    // ... 原有属性 ...

    public Employee Clone() {
        // MemberwiseClone会创建新实例,复制所有值类型字段;引用类型字段会复制引用
        return (Employee)this.MemberwiseClone();
    }
}

注意:如果类中有其他引用类型的属性(比如另一个自定义类),浅拷贝会导致副本和原对象共享这些引用,此时需要改成深拷贝。

验证撤销功能

修改后,当你在PropertyGrid中修改属性,栈里保存的是修改前的独立副本,点击btnReset时,PropertyGrid会加载栈中的副本,就能实现正确的撤销效果了。

内容的提问来源于stack exchange,提问作者tfeitsma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:07:23