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

如何保持Revit插件中_start_state字典值不随文档修改变更?

问题:Revit中存储的初始Location随元素修改而变更

需求与问题

  • 需求:
    1. Revit打开文档时,将所有追踪元素的ElementId与Location存入_start_state字典
    2. 文档发生变更时,通过修改元素的ElementId从_start_state中获取原始Location
  • 问题:触发CtrlApp_DocumentChanged事件(元素被修改)时,_start_state中对应ElementId的Location值会同步变化,无法保留打开文档时的初始状态

相关代码

Dictionary<ElementId, Location> _start_state;
List<ElementId> startKeys;

public void CtrlApp_DocumentOpened(object sender, DocumentOpenedEventArgs e)
{
    // 获取当前文档
    Document doc = e.Document;

    IEnumerable<Element> a = GetTrackedElements(doc);

    Dictionary<ElementId, Location> start_state;
    start_state = GetSnapshot(a);

    _start_state = new Dictionary<ElementId, Location>(start_state);
    
    startKeys = _start_state.Keys.ToList();
}

public void CtrlApp_DocumentChanged(object sender, DocumentChangedEventArgs e)
{
    ICollection<ElementId> modifiedElem = e.GetModifiedElementIds();
    
    foreach (ElementId id in modifiedElem)
    {
        if (startKeys.Contains(id)) // 此处获取到的是修改后的Location而非原始值
        {
            Dictionary<ElementId, Location> dict = new Dictionary<ElementId, Location>();
            List<Location> locList = new List<Location>();
            locList.Add(_start_state[id]);
            
            foreach (Location loc in locList)
            {
                send_baseLocation(loc);
            }
        }
    }
}

可行解决方案建议

核心原因

问题本质是Location是Revit元素的绑定实例,属于引用类型。你存储的是该实例的引用,当元素位置修改时,Location实例的内部属性(如坐标、曲线)会被Revit更新,导致字典中存储的引用指向的对象也跟着变化。


方案1:提取并存储位置的不可变数据(推荐)

不要直接存储Location对象,而是提取其内部的具体值(值类型或克隆后的不可变对象),用自定义类保存:

  1. 定义存储位置信息的类:
public class SavedLocation
{
    // 存储点位置(XYZ是值类型,赋值时会复制)
    public XYZ Point { get; set; }
    // 存储曲线位置(克隆曲线避免引用原对象)
    public Curve Curve { get; set; }
    // 标记当前位置类型
    public LocationType Type { get; set; }
}

public enum LocationType
{
    Point,
    Curve
}
  1. 修改GetSnapshot方法,转换并保存不可变数据:
public Dictionary<ElementId, SavedLocation> GetSnapshot(IEnumerable<Element> elements)
{
    var snapshot = new Dictionary<ElementId, SavedLocation>();
    foreach (var elem in elements)
    {
        var loc = elem.Location;
        if (loc is LocationPoint pointLoc)
        {
            snapshot.Add(elem.Id, new SavedLocation
            {
                Type = LocationType.Point,
                Point = pointLoc.Point // XYZ是值类型,直接复制
            });
        }
        else if (loc is LocationCurve curveLoc)
        {
            snapshot.Add(elem.Id, new SavedLocation
            {
                Type = LocationType.Curve,
                Curve = curveLoc.Curve.Clone() // 克隆曲线,生成独立实例
            });
        }
    }
    return snapshot;
}
  1. 更新全局字典类型及事件处理逻辑:
// 修改全局字典类型
Dictionary<ElementId, SavedLocation> _start_state;

// CtrlApp_DocumentOpened中对应修改
_start_state = GetSnapshot(a);

// CtrlApp_DocumentChanged中获取原始位置
foreach (ElementId id in modifiedElem)
{
    if (_start_state.ContainsKey(id))
    {
        var savedLoc = _start_state[id];
        if (savedLoc.Type == LocationType.Point)
        {
            // 处理点位置
            send_baseLocation(savedLoc.Point);
        }
        else
        {
            // 处理曲线位置
            send_baseLocation(savedLoc.Curve);
        }
    }
}

方案2:结合ImmutableDictionary(仅辅助保证字典结构不可变)

单独使用ImmutableDictionary无法解决问题,因为它仅保证字典的键值对结构不可修改,但存储的Location引用仍会指向可变的Revit实例。必须配合方案1的值类型存储一起使用:

// 使用ImmutableDictionary存储SavedLocation
ImmutableDictionary<ElementId, SavedLocation> _start_state;

// 初始化时转换为不可变字典
_start_state = GetSnapshot(a).ToImmutableDictionary();

这样既保证字典结构不会被意外修改,又保证存储的位置数据是独立的初始值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 23:35:42