【发布时间】:2016-01-03 01:36:14
【问题描述】:
我有一个用户控件,它将inner 对象列表订阅到另一个组件中的对象。有时我想根据用户控件的属性设置更改删除 inner 对象。如果我盲目地从列表中删除它们而不取消订阅,我认为这会导致孤立的inner 对象的内存泄漏。
有时我可能无法访问我们订阅时拥有的对象列表。例如,我订阅的项目列表可能已更改,这是我取消订阅的原因之一。
我当然可以在我的代码中为每个内部对象添加对订阅对象的引用,但我有兴趣看看是否有可以利用的内置机制。
更新 #1:
汉斯要求一些代码。我制作了一个示例,我认为该示例表明,当列表被清除时,innerObject 不会被释放。我认为这相当于将它们的引用设置为 null。
/*
* Program that demonstrates that event subscribers stay alive after
* loosing scope.
*
* The question is asking if I don't have a reference to myObject is there a way to
* unsubscribe with what the innerObject knows natively.
*
* In the Unsubscribe() method I have examples of unsubscribing using a "known" myObject
* and one with a self reference to the myObject.
*
* The solution that uses IObserver and IObservable automates a way to store an explicit
* reference to the subscription holder.
*
*/
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
/// <summary>
/// Sample objects that subscribe to MyObject's Updated event
/// </summary>
public class InnerObject
{
public MyObject Subscribed { get; set; }
public void Updated(object sender, MyObjectUpdateEventArgs e)
{
Console.WriteLine(e.Data);
}
}
/// <summary>
/// Object that publishes the Updated event
/// </summary>
public class MyObject
{
private string data_;
public event EventHandler<MyObjectUpdateEventArgs> Updated;
public string Data { get { return data_; } set { SetMyData(value); } }
/// <summary>
/// Outputs the Data string
/// </summary>
/// <returns></returns>
public override string ToString()
{
return Data;
}
/// <summary>
/// Event handler
/// </summary>
/// <param name="e"></param>
private void OnUpdate(MyObjectUpdateEventArgs e)
{
EventHandler<MyObjectUpdateEventArgs> handler = Updated;
if (Updated != null) {
handler(this, e);
}
}
private void SetMyData(string value)
{
if (Data != value) {
data_ = value;
OnUpdate(new MyObjectUpdateEventArgs(Data));
}
}
}
/// <summary>
/// EventArgs to provide updated MyData value;
/// </summary>
public class MyObjectUpdateEventArgs
{
public MyObjectUpdateEventArgs(string data)
{
Data = data;
}
public string Data { get; set; }
}
internal class Program
{
private static List<InnerObject> innerObjectsList = new List<InnerObject>();
private static void Main(string[] args)
{
MyObject myObject = new MyObject();
myObject.Data = "Hello World";
Console.WriteLine(myObject.ToString());
Console.ReadLine();
// Create the innerObjectts and subscribe
MakeNewInnerObjects(5, myObject);
// This will cause all of the inner objects to respond with myObjects Data string
Console.WriteLine("Assigning new data to myObject\n");
myObject.Data = "Hello InnerObjects";
// Shows they are responding, even though the list and the items are out of scope
Console.ReadLine();
// Uncomment to unsubscribe
// Unsubscribe(myObject);
innerObjectsList.Clear();
// Force garbage collection for our example
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Assigning new data to myObject\n");
myObject.Data = "Are you still there InnerObjects?";
// Shows they still exist if we don't unsubscribe
Console.ReadLine();
}
/// <summary>
/// Create a count of InnerObjects, subscribe them to myObject and add to the list
/// </summary>
/// <param name="count"></param>
private static void MakeNewInnerObjects(int count, MyObject myObject)
{
// Uncomment if you want to have the list go out of scope as well to show
// That the reference keeps them alive
//List<InnerObject> innerObjectsList = new List<InnerObject>();
for (int i = 0; i < count; i++) {
InnerObject innerObject = new InnerObject();
innerObject.Subscribed = myObject;
myObject.Updated += innerObject.Updated;
innerObjectsList.Add(innerObject);
}
}
// Unsubscribe the list of innerObJects from MyObject
private static void Unsubscribe(MyObject myObject)
{
// Two ways to unsubscribe, the first depends on knowing what we subscribbed to
// the second uses a stored reference to the object
foreach (InnerObject innerObject in innerObjectsList) {
// myObject.Updated -= innerObject.Updated;
innerObject.Subscribed.Updated -= innerObject.Updated;
}
}
}
}
【问题讨论】:
-
没有sn-p相当模糊,但没有明显的“泄漏”危险,这些“内部对象”通过事件订阅保持对您的UC的引用。换句话说,这些对象让你的 UC 保持活力,而不是相反。如果您的 UC 保留对此类内部对象的引用,那么您只需将其设置为 null。
-
如果内部对象从控件列表中删除,对它们的唯一引用将通过它们订阅的 MyObject 事件。这不会让内部对象保持活力吗?
标签: c# events memory-leaks