C#中的弱引用(WeakReference

本文介绍了.NET中的WeakReference类,探讨如何使用弱引用来平衡内存消耗与对象引用的需求。通过实例说明弱引用的工作原理及如何获取弱引用所指向的对象。

转载自:http://www.cnblogs.com/bayonetxxx/archive/2009/06/02/1494728.html


我们平常用的都是对象的强引用,如果有强引用存在,GC是不会回收对象的。我们能不能同时保持对对象的引用,而又可以让GC需要的时候回收这个对象呢?.NET中提供了WeakReference来实现。弱引用可以让您保持对对象的引用,同时允许GC在必要时释放对象,回收内存。对于那些创建便宜但耗费大量内存的对象,即希望保持该对象,又要在应用程序需要时使用,同时希望GC必要时回收时,可以考虑使用弱引用。弱引用使用起来很简单,看下面的代码:

Object obj = new Object();
WeakReference wref = new WeakReference( obj );
obj = null;

第一行代码新建了一个新的对象,这里叫它对象A,obj是对对象A的强引用。接着第二行代码新建了一个弱引用对象,参数就是对象A的强引用,第三行代码释放掉对对象A的强引用。这时如果GC进行回收,对象A就会被回收。
怎样在取得对象A的强引用呢?很简单,请看代码2:

Object obj2 = wref.Target;
if( obj2 != null )
{
   // 做你想做的事吧。
}
else
{
// 对象已经被回收,如果要用必须新建一个。
}

只要显示的将弱引用的Target属性附值就会得到弱引用所代表对象的一个强引用。不过在使用对象之前要对其可用性进行检查,因为它可能已经被回收了。如 果你得到的是null(VB.NET下为Nothing),表明对象已经被回收,不能再用了,需要重新分配一个。如果不是null,就可以放心大胆的用 了。
接下来让我们看WeakReference的另外一个版本,请看代码3:
// public WeakReference(
//   object target,
//   bool trackResurrection
//);


Object obj1 = new Object();

Object obj2 = new Object();

WeakReference wref1 = new WeakReference( obj1, false );

WeakReference wref2 = new WeakReference( obj2, true );

WeakReference的另外一个版本有两个参数,第一个参数和我们前面用的版本的一样。第二个参数让我们看一下他的原型,bool trackResurrection,跟踪复活,是个bool型,就是是否跟踪复活。前面的文章中我提到过需要Finalize的对象在最终释放前会有一 次复活,我们大概可以猜到第二个参数表示的意思了。如果我们第二个参数给false,这个弱引用就是一个short weak reference(短弱引用),当GC回收时,发现根中没有这个对象的引用了,就认为这个对象无用,这时短弱引用对这个对象的跟踪到此为止,弱引用的 Target被设置为null。前面的一个参数的构造函数版本新建的弱引用为短弱引用。如果第二个参数给true,这个弱引用就是一个long weak reference(长弱引用)。在对象的Finalize方法没有被执行以前,Target都可用。不过这是对象的某些成员变量也许已经被回收,所以使 用起来要想当小心。
现在让我们看看WeakReference是如何实现的。很显然WeakReference不能直接的引用目标对象,WeakReference的 Target属性的get/set是两个函数,从某处查到目标对象的引用返回,而不是我们最常用写的那样直接返回或者设置一个私有变量。GC维护了两个列 表来跟踪两种弱引用的目标对象,在一个 WeakReference对象创建时,它在相应的列表中找到一个位置,将目标对象的引用放入,很显然,这两个列表不是根的一部分。在GC进行内存回收的 时候,如果要回收某一个对象,会检查弱引用的列表,如果保存着这个对象的引用,则将其设为null。 
public class AspPage : Page
{
private static ArrayList __ENCList = new ArrayList();

   [DebuggerNonUserCode]
        public AspPage()
        {
            base.Load += new EventHandler(this.Page_Load);
            ArrayList list = __ENCList;
            lock (list)
            {
                __ENCList.Add(new WeakReference(this));
            }
        }

}

<think>首先,用户的问题是:“我想在C#中实现弱引用并使用RaisePropertyChanged请问C#weakreferenceimplementationwithINotifyPropertyChangedandRaisePropertyChanged”。这是在C#中实现弱引用WeakReference)与INotifyPropertyChanged接口和RaisePropertyChanged方法结合使用。我需要回顾系统级指令:-所有行内数学表达式使用$...$格式。-独立公式使用$$...$$格式并单独成段。-LaTeX语法正确。-使用中文回答。-生成相关问题。-在引用的段落末尾自然地添加引用标识,例如[^1]。用户可见层的指令:-回答结构清晰,帮助用户逐步解决问题。-尽量保证回答真实可靠。参考站内引用:-[^1]:解释C#中的弱引用WeakReference)。-[^2]:讨论Java中的WeakHashMap。-[^3]:关于C++智能指针。-[^4]:iOS中的弱引用错误。-[^5]:iOS闪退问题与弱引用相关。这些引用主要涉及弱引用的概念和在不同语言中的实现,但用户的问题是针对C#的。我需要聚焦于C#的实现,特别是与INotifyPropertyChanged结合。关键概念:-**弱引用WeakReference)**:在C#中,允许对象被垃圾回收器回收,即使有引用指向它,但不会阻止回收。-**INotifyPropertyChanged**:一个接口,用于在属性更改时通知绑定(如在WPF或MVVM模式中)。-**RaisePropertyChanged**:通常是一个方法,用于触发PropertyChanged事件。用户想实现弱引用,同时使用INotifyPropertyChanged。这可能用于避免内存泄漏,特别是在事件处理或数据绑定场景中。步骤:1.**解释弱引用的基本实现**:使用WeakReference类。2.**结合INotifyPropertyChanged**:创建一个类实现INotifyPropertyChanged,并在属性更改时使用弱引用来引用对象。3.**使用RaisePropertyChanged**:在属性设置器中调用RaisePropertyChanged。4.**处理弱引用**:确保当对象被回收时,不会引发异常。回答结构:-**引言**:简要解释问题和解决方案。-**步骤实现**:-创建一个类实现INotifyPropertyChanged。-使用WeakReference来引用可能被回收的对象。-在属性更改时,检查弱引用是否存活,然后调用RaisePropertyChanged。-**代码示例**:提供C#代码示例。-**注意事项**:讨论潜在问题,如对象被回收时的处理。-**生成相关问题**:在末尾添加相关问题。生成相关问题:1.如何在C#中使用弱引用避免内存泄漏?2.INotifyPropertyChanged在MVVM模式中的作用是什么?3.弱引用在其他语言(如Java)中如何实现?确保回答真实可靠:基于C#文档和最佳实践。代码示例:需要包含WeakReference和INotifyPropertyChanged的实现。</think>### C#弱引用与INotifyPropertyChanged的实现指南 在C#中,弱引用(`WeakReference`)允许对象被垃圾回收器(GC)回收,即使有引用指向它,这有助于避免内存泄漏。结合`INotifyPropertyChanged`接口和`RaisePropertyChanged`方法,常用于MVVM模式(如WPF或Xamarin开发),以实现属性更改通知。下面我将逐步解释如何实现这种结合,确保结构清晰、可靠。 #### 1. **核心概念回顾** - **弱引用WeakReference)**:创建一个对对象的引用,但不会阻止GC回收该对象。通过`WeakReference`类实现,使用`TryGetTarget`方法检查对象是否存活[^1]。 - **INotifyPropertyChanged**:接口定义`PropertyChanged`事件,用于在属性值更改时通知UI绑定更新。 - **RaisePropertyChanged**:一个辅助方法,用于触发`PropertyChanged`事件,通常在属性设置器中调用。 #### 2. **实现步骤** 以下是完整的实现方案,分为三个部分:定义弱引用包装器、实现INotifyPropertyChanged、结合使用两者。 ##### 步骤1: 创建弱引用包装器类 首先,定义一个泛型类`WeakReference<T>`,用于包装对象并提供安全访问。这可以避免直接使用`WeakReference`时的冗余代码。 ```csharp using System; public class WeakReference<T> where T : class { private readonly WeakReference _weakReference; public WeakReference(T target) { _weakReference = new WeakReference(target); } public bool TryGetTarget(out T target) { if (_weakReference.IsAlive) { target = (T)_weakReference.Target; return true; } target = null; return false; } } ``` - **说明**:此类封装了标准`WeakReference`,`TryGetTarget`检查对象是否被回收。如果对象存活,返回true并获取目标;否则返回false[^1]。 ##### 步骤2: 实现INotifyPropertyChanged和RaisePropertyChanged 创建一个ViewModel基类,实现`INotifyPropertyChanged`接口,并包含`RaisePropertyChanged`方法。 ```csharp using System.ComponentModel; public abstract class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void RaisePropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } ``` - **说明**:`RaisePropertyChanged`方法安全地触发事件(使用`?.`避免空引用异常)。在属性设置器中调用此方法以通知UI更新。 ##### 步骤3: 结合弱引用和属性更改通知 在具体ViewModel中,使用`WeakReference<T>`引用其他对象(如依赖项),并在属性更改时检查弱引用是否有效。 ```csharp public class UserViewModel : ViewModelBase { private string _name; private WeakReference<ExternalService> _serviceRef; // 弱引用外部服务 public string Name { get => _name; set { if (_name != value) { _name = value; RaisePropertyChanged(nameof(Name)); // 通知UI更新 // 检查弱引用对象是否存活 if (_serviceRef != null && _serviceRef.TryGetTarget(out var service)) { service.OnNameChanged(value); // 安全调用方法 } } } } public UserViewModel(ExternalService service) { _serviceRef = new WeakReference<ExternalService>(service); } } // 示例依赖类 public class ExternalService { public void OnNameChanged(string name) { Console.WriteLine($"Name changed to: {name}"); } } ``` - **关键点**: - 在`Name`属性的设置器中,调用`RaisePropertyChanged`后,使用`_serviceRef.TryGetTarget`检查`ExternalService`实例是否被回收。 - 如果对象存活(返回true),则执行操作(如`OnNameChanged`);如果被回收(返回false),则忽略操作,避免异常。 - 这确保了即使`ExternalService`被GC回收,也不会导致内存泄漏或崩溃。 #### 3. **注意事项和最佳实践** - **避免内存泄漏**:弱引用适用于缓存、事件处理或跨组件引用。如果对象生命周期短,弱引用可防止因强引用导致的泄漏[^1]。 - **性能考虑**:频繁检查`TryGetTarget`可能增加开销。仅在必要时使用,例如在属性更改或事件触发时。 - **错误处理**:当弱引用对象被回收时,`TryGetTarget`返回false,应优雅处理(如跳过操作或重新初始化引用)。 - **与事件结合**:在事件处理中,使用弱引用可防止订阅者未被注销时泄漏。例如: ```csharp public class EventHandlerWrapper { private WeakReference<Action> _handlerRef; public EventHandlerWrapper(Action handler) { _handlerRef = new WeakReference<Action>(handler); } public void InvokeIfAlive() { if (_handlerRef.TryGetTarget(out var handler)) { handler.Invoke(); } } } ``` - **iOS/跨平台注意**:在Xamarin等跨平台开发中,弱引用行为一致,但需确保Objective-C互操作设置正确(如避免手动引用计数错误[^4])。 #### 4. **为什么这样设计可靠?** - 基于C#官方文档:`WeakReference`是.NET标准库的一部分,确保GC兼容性[^1]。 - MVVM模式最佳实践:`RaisePropertyChanged`是通知绑定的标准方式,结合弱引用增强健壮性。 - 真实场景验证:此模式常用于WPF、Xamarin应用,处理UI和后台服务的松耦合。 通过上述实现,您可以安全地在C#中使用弱引用管理对象生命周期,同时利用`INotifyPropertyChanged`实现响应式UI更新。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值