Recursive Enumerator

using System;
using System.Collections.Generic;

namespace SecretNest.RecursiveEnumerator
{
    /// <summary>
    /// Get the enumerator for querying the parents of specified item.
    /// </summary>
    /// <typeparam name="T">Item type</typeparam>
    /// <param name="current">Item for querying parents</param>
    /// <returns>Enumerator of parents querying</returns>
    public delegate IEnumerator<T> GetParentsEnumerator<T>(T current);

    /// <summary>
    /// Enumerator for querying parents
    /// </summary>
    /// <typeparam name="T">Item type</typeparam>
    public class Enumerator<T> : IEnumerator<T>
    {
        T current, initial;

        Queue<T> notQueried = new Queue<T>();

        HashSet<T> queried = new HashSet<T>(); //for avoiding duplicated query
        Queue<T> rollbackHistory = new Queue<T>(); //for soft reset
        Queue<T> history = new Queue<T>(); //for soft reset
        IEnumerator<T> activeQuery;

        /// <summary>
        /// Callback for getting the enumerator, which is used for querying the parents of specified item.
        /// </summary>
        public GetParentsEnumerator<T> GetParentsEnumeratorCallback { get; set; }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="initial">Initial item</param>
        public Enumerator(T initial)
        {
            notQueried.Enqueue(initial);
            this.initial = initial;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="initial">Initial item</param>
        /// <param name="callback">Callback for getting the enumerator, which is used for querying the parents of specified item.</param>
        public Enumerator(T initial, GetParentsEnumerator<T> callback)
        {
            notQueried.Enqueue(initial);
            this.initial = initial;
            GetParentsEnumeratorCallback = callback;
        }

        /// <summary>
        /// Gets the current element in the collection.
        /// </summary>
        public T Current
        {
            get { return current; }
        }

        bool disposed;
        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    current = default(T);
                    notQueried = null;
                    queried = null;
                    history = null;
                }
                disposed = true;
            }
        }

        /// <summary>
        /// Gets the current element in the collection.
        /// </summary>
        object System.Collections.IEnumerator.Current
        {
            get { return current; }
        }

        /// <summary>
        /// Skip same items
        /// </summary>
        public bool SkipSameItems { get; set; }

        /// <summary>
        /// Advances the enumerator to the next element of the collection.
        /// </summary>
        /// <returns>true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. </returns>
        public bool MoveNext()
        {
            if (disposed) throw new ObjectDisposedException(null);
            if (rollbackHistory.Count > 0)
            {
                current = rollbackHistory.Dequeue();
                history.Enqueue(current);
                return true;
            }
            if (activeQuery != null)
            {
            here:
                if (activeQuery.MoveNext())
                {
                    if (SkipSameItems && history.Contains(activeQuery.Current)) { goto here; }
                    current = activeQuery.Current;
                    history.Enqueue(current);
                    notQueried.Enqueue(current);
                    return true;
                }
                else
                {
                    activeQuery = null;
                }
            }
            if (GetParentsEnumeratorCallback != null)
            {
                while (notQueried.Count != 0)
                {
                    T item = notQueried.Dequeue();
                    if (!queried.Contains(item))
                    {
                        IEnumerator<T> enumerator = GetParentsEnumeratorCallback(item);
                        queried.Add(item);
                        here:
                        if (enumerator != null)
                        {
                            if (enumerator.MoveNext())
                            {
                                activeQuery = enumerator;
                                if (SkipSameItems && history.Contains(enumerator.Current)) { goto here; }
                                current = enumerator.Current;
                                history.Enqueue(current);
                                notQueried.Enqueue(current);
                                return true;
                            }
                            else
                            {
                                enumerator = null;
                            }
                        }
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Sets the enumerator to its initial position, which is before the first element in the collection. Keep all histories for caching.
        /// </summary>
        public void Reset()
        {
            if (disposed) throw new ObjectDisposedException(null);
            while (history.Count > 0)
            {
                rollbackHistory.Enqueue(history.Dequeue());
            }
            current = default(T);
        }

        /// <summary>
        /// Sets the enumerator to its initial position, which is before the first element in the collection. Reset all data, and close active sub-query.
        /// </summary>
        public void HardReset()
        {
            if (disposed) throw new ObjectDisposedException(null);
            queried.Clear();
            notQueried.Clear();
            notQueried.Enqueue(initial);
            history.Clear();
            activeQuery = null;
            current = default(T);
        }
    }
}

Download code and demo projects

TreeView with CheckBox

If you wanna use CheckBox enabled TreeView in Windows Vista and further systems, you need to pay attention. If you double click the CheckBoxes in the TreeView, some useful event handlers, like BeforeClick and AfterClick, will not be raised. And, the vision of CheckBoxes will be changed (checked or not) but the property of related TreeNode will not.

To fix this, you have to create your own TreeView instead of the provided one.

using System;
using System.Windows.Forms;

[
        ComVisible(true),
        ClassInterface(ClassInterfaceType.AutoDispatch),
        DefaultProperty("Nodes"),
        DefaultEvent("AfterSelect"),
        Docking(DockingBehavior.Ask),
        Designer("System.Windows.Forms.Design.TreeViewDesigner"),
]
public class FixedTreeView : TreeView
{
	protected override void WndProc(ref Message m)
	{
		// Suppress WM_LBUTTONDBLCLK
		if (m.Msg == 0x203)
		{
			m.Result = IntPtr.Zero;
		}
		else
		{
			base.WndProc(ref m);
		}
	}
}

Source: http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/9d717ce0-ec6b-4758-a357-6bb55591f956

Weak Reference

本文简述dotNet中Weak Reference(弱引用)的功能,并举例在具体应用场景中的效果。

 

首先,让我们回忆一下dotNet CLR的对象生命周期管理。为了解决对象引用计数器以及编码不慎可能导致的内存泄漏,dotNet CLR引入了Garbage Collection(GC)机制。GC会不时的扫描程序,将程序中不再使用的对象进行回收。

那么,怎样的对象才是不再被使用的呢?通常的,我们可以简单认为,在未关联至任何变量(包括对象属性、集合内成员等)的情况下的对象,也就是说,无法被安全的访问到的对象,会被认为是不再被使用的对象。那么,有没有例外呢?

这个例外,就是dotNet中的Weak Reference。它的直接功能,就是封装对一个对象的引用,但不会被GC认做有效的关联。换言之,GC在判断对象是否不再被使用时,不会考虑Weak Reference的引用。

Weak Reference类位于System命名空间下。我们可以通过其构造器直接构造一个对象。System.WeakReference有两个构造器,通常我们使用的第一个,要求传送一个object作为其引用的实际对象(Target属性);第二个构造器需要传送一个bool类型变量,用以定义是否进行长程跟踪,本功能不在本文中进行描述。

当我们构造好一个System.WeakReference后,我们可以通过访问其Target属性,设置或读取其引用的实际变量。由于GC不会考虑Weak Reference中对象的引用,如果Target属性的对象仅被引用于Weak Reference对象时,这个对象则随时可能被GC回收。

我们可以通过判断Weak Reference对象的IsAlive属性,来确定Target属性指向的对象是否已经被回收。当IsAlive为false时,Target属性也将被自动赋值为null。特别的,由于Target属性也可以被显式的赋值为null,我们应该使用IsAlive来进行判断,而不要使用Target属性是否为null来推断。

 

那么,Weak Reference有什么实际用途呢?

有时候,我们需要访问一个对象,却又不想管理其生命周期。例如,我们可能在要某段代码中,将某个窗体对象赋值至一个对象属性中,但我们又不希望由于这个对象未被回收,影响到原窗体的生命周期。如果我们使用Weak Reference对象,代替原属性值,即可避免这种情况的出现。

另外,有些数据,我们并不会经常的访问,但所有的访问又相对密集。这时候我们也可以使用Weak Reference来对数据进行封装。每次访问时,通过判断IsAlive来确定,是否需要构造其Target。由于GC通常不会在系统繁忙时激活,所以这种类似Cache的应用,通常只会在资源不足或系统空闲时才会被回收,而且这种回收是由dotNet CLR负责实现,无需用户编码干预。

Chorus本地模式版已经发布

Chorus是一个框架,它实现了多个部件之间的通讯功能。在它的协助下,软件系统中的每个部件可以自由通讯,而不论这些部件运行在同一个程序,同一台计算机,还是在世界的任何角落。

Chorus是一种模型,它展示了软件系统内部的物理结构。在它的支持下,软件系统中的每个部件可以随心拼装,而不论这些部件构造于您的产品,您的企业,还是获取自其他供应商。

Chorus是一份标准,它定义了每个程序部件的接口规格。在它的规范下,软件系统中的每个部件可以严格定义,而不论这些部件开发于同一个员工,同一个小组,还是来自外包公司。

 

当前已经发布本地模式版,实现同一个应用程序内部的通讯功能。

您有兴趣了解吗?请访问Chorus发布页