C#如何在类中存储struct的ref引用?WPF客户端信息编辑场景问题
C# 不允许将ref引用存储到类的属性或字段中,本质是ref引用的生命周期被限制为仅在当前方法调用栈内有效,而类是堆上对象,生命周期可能长于栈上的struct实例,强制存储会引发内存安全风险。以下是几种可行的实现方案:
方案1:将值类型struct改为引用类型class(最推荐,无额外复杂度)
如果没有必须使用struct的硬性要求,直接修改ClientInfos的定义为class即可,引用类型天然支持在类中存储实例引用:
// 修改为class public class ClientInfos { public string Firstname { get; set; } public string Lastname { get; set; } public int Age { get; set; } }
对应的调用和ViewModel代码同步简化,无需ref传递:
// 窗口调用 ClientInfosWindow.Show(clientInfos); // ViewModel实现 class ClientInfosViewModel { private readonly ClientInfos _originalClientInfos; public ClientInfos ClientInfosTemp { get; set; } public ClientInfosViewModel(ClientInfos clientInfos) { _originalClientInfos = clientInfos; // 拷贝初始值到临时对象 ClientInfosTemp = new ClientInfos { Firstname = clientInfos.Firstname, Lastname = clientInfos.Lastname, Age = clientInfos.Age }; } private void SaveClientInfos() { // 同步临时值到原始实例 _originalClientInfos.Firstname = ClientInfosTemp.Firstname; _originalClientInfos.Lastname = ClientInfosTemp.Lastname; _originalClientInfos.Age = ClientInfosTemp.Age; ClientInfosWindow.Close(); } }
方案2:使用引用类型包装struct(适合必须保留struct的场景)
如果确实需要保留ClientInfos为struct,可以封装一个泛型引用类型包装类承载struct值:
// 通用值类型包装类 public class ValueWrapper<T> where T : struct { public T Value { get; set; } public ValueWrapper(T value) => Value = value; }
调用时传递包装类实例:
// 外层定义包装实例 var clientWrapper = new ValueWrapper<ClientInfos>(clientInfos); ClientInfosWindow.Show(clientWrapper);
ViewModel中存储包装类引用即可:
class ClientInfosViewModel { private readonly ValueWrapper<ClientInfos> _originalWrapper; public ClientInfos ClientInfosTemp { get; set; } public ClientInfosViewModel(ValueWrapper<ClientInfos> wrapper) { _originalWrapper = wrapper; ClientInfosTemp = wrapper.Value; } private void SaveClientInfos() { _originalWrapper.Value = ClientInfosTemp; ClientInfosWindow.Close(); } } // 外层使用时取clientWrapper.Value即为更新后的值
方案3:使用回调委托回传结果(解耦性最优,符合MVVM设计规范)
不需要在ViewModel中存储原始实例引用,通过委托将更新后的值回传给调用方:
// 窗口调用时传入回调 ClientInfosWindow.Show(clientInfos, updatedInfos => { clientInfos = updatedInfos; });
ViewModel实现:
class ClientInfosViewModel { private readonly Action<ClientInfos> _saveCallback; public ClientInfos ClientInfosTemp { get; set; } public ClientInfosViewModel(ClientInfos originalInfos, Action<ClientInfos> saveCallback) { _saveCallback = saveCallback; ClientInfosTemp = originalInfos; } private void SaveClientInfos() { _saveCallback?.Invoke(ClientInfosTemp); ClientInfosWindow.Close(); } }
不推荐方案:unsafe指针
如果一定要操作原始struct的内存地址,可以开启unsafe上下文用指针存储,但会引入内存安全风险,不建议在业务代码中使用:
unsafe class ClientInfosViewModel { private ClientInfos* _originalPtr; public ClientInfos ClientInfosTemp { get; set; } public unsafe ClientInfosViewModel(ref ClientInfos clientInfos) { fixed (ClientInfos* ptr = &clientInfos) { _originalPtr = ptr; } ClientInfosTemp = clientInfos; } private unsafe void SaveClientInfos() { *_originalPtr = ClientInfosTemp; ClientInfosWindow.Close(); } }
内容的提问来源于stack exchange,提问作者Maxime Charrière
相关产品推荐
相关产品推荐

