fail-fast
俗称快速失败策略,具体来说就是指用迭代器在遍历一个集合时对被遍历的元素进行增删改一类的set操作会导致系统抛出InvalidOperationException(Java里面是抛出ConcurrentModificationException)。
有关快速失败的原理这里就不多做解释,有兴趣的老铁可以浏览下面的链接博客。
https://www.jianshu.com/p/1c2d31b1f69e
C#event
在上篇文章讲述了C#中委托,匿名方法,Lambda表达式之间的关系并且知道后两者都是基于委托而实现的,事件event也是基于委托而实现。而在真正的实现上,可以把C#的event近似的看成js中的event,都是通过函数作为参数进行传递,同时也都有“接事件与发事件”的过程。
实现思路
明白了fail-fast和event的原理概念后,实现的思路其实也就不难了:
定义一个内部类,实现IEnumerator<T>接口,定义一个私有指针用于记录迭代器的遍历位置,然后在原生类里定义一个事件,并把事件绑定于每一个能修改元素的方法上,最后在此内部类里定义一个私有的判断指针和一个接收事件,接收事件改变判断指针的属性并且在迭代方法上通过判断指针进行fail-fast判断。
第一步:构建一个内部类并实现IEnumerator<T>接口
class NodeEnumerator : IEnumerator<T>
{
private int _actualIndex = -1;
public T Current => throw new NotImplementedException();
object IEnumerator.Current => throw new NotImplementedException();
public void Dispose()
{
throw new NotImplementedException();
}
public bool MoveNext()
{
throw new NotImplementedException();
}
public void Reset()
{
throw new NotImplementedException();
}
}
其中有两个属性三个方法是需要被实现(_actualIndex 是我们自己添加的属性),具体是什么我不详细讲述,有兴趣的老铁可以自行去百度研究这里,我挑重点进行叙述。
MoveNext():在这个方法里面判断迭代器是否终止,同时也是在这个方法里面判断是否执行fail-fast。
Reset(): 这个方法用于重置迭代器(_actualIndex = -1)。
Current : 这个属性返回当前迭代器正在被遍历的元素(这个属性只能对外提供get方法)。
第二步:插入事件
原生类:
private void OnChanged() =>
Changed?.Invoke(this, EventArgs.Empty);
private event EventHandler Changed;
内部类:
private bool _changed = false;
private void OnChanged(object sender, EventArgs e)
{
_changed = true;
}
最后一步: 绑定事件
原生类:
public void Add(T item)
{
OnChanged();
var itemNode = new Node(item);
if (_head == null)
{
_head = itemNode;
}
else
{
var iNode = _head;
for (; iNode.next != null; iNode = iNode.next)
{
}
iNode.next = itemNode;
}
Count++;
}
内部类:
public NodeEnumerator(MyLinkedList<T> linkedList)
{
_mylinkedList = linkedList;
_mylinkedList.Changed += OnChanged;
Reset();
}
public bool MoveNext()
{
if (_changed)
{
throw new InvalidOperationException();
}
_actualIndex++;
return (_mylinkedList.Count > _actualIndex);
}
完整代码
最后我附上以单链表为环境的全部源码,希望能对大家有所帮助。
public class MyLinkedList<T> : Object, IList<T>
{
class Node
{
public T data;
public Node next;
public Node(T data)
{
this.data = data;
}
}
private Node _head;
public MyLinkedList()
{
_head = null;
}
//获取和设置指定处的元素
public T this[int index]
{
get
{
return SearchIndex(index).data;
}
set
{
OnChanged();
SearchIndex(index).data = value;
}
}
//获取当前链表中元素的个数
public int Count { get; set; }
public bool IsReadOnly { get { return false; } }
//发事件,接事件,类比js的event
private void OnChanged() =>
Changed?.Invoke(this, EventArgs.Empty);
private event EventHandler Changed;
class NodeEnumerator : IEnumerator<T>
{
private int _actualIndex = -1;
private bool _changed = false;
private MyLinkedList<T> _mylinkedList;
private void OnChanged(object sender, EventArgs e)
{
_changed = true;
}
public NodeEnumerator(MyLinkedList<T> linkedList)
{
_mylinkedList = linkedList;
_mylinkedList.Changed += OnChanged;
Reset();
}
public T Current
{
get
{
try
{
T element = _mylinkedList[_actualIndex];
return element;
}
catch (IndexOutOfRangeException e)
{
throw new InvalidOperationException();
}
}
}
object IEnumerator.Current => throw new NotImplementedException();
public void Dispose()
{
GC.SuppressFinalize(this);
}
public bool MoveNext()
{
if (_changed)
{
throw new InvalidOperationException();
}
_actualIndex++;
return (_mylinkedList.Count > _actualIndex);
}
public void Reset()
{
_actualIndex = -1;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
//UnitTest
public IEnumerator<T> GetEnumerator()
{
return new NodeEnumerator(this);
}
//UnitTest
public void Add(T item)
{
OnChanged();
var itemNode = new Node(item);
if (_head == null)
{
_head = itemNode;
}
else
{
var iNode = _head;
for (; iNode.next != null; iNode = iNode.next)
{
}
iNode.next = itemNode;
}
Count++;
}
public void Clear()
{
OnChanged();
while (_head.next != null)
{
var nextNode = _head.next;
_head.next = nextNode.next;
}
_head = null;
Count = 0;
}
public bool Contains(T item)
{
return IndexOf(item) != -1;
}
public void CopyTo(T[] array, int arrayIndex)
{
if (array == null)
{
throw new ArgumentNullException();
}
for (int i = 0; i < Count; i++)
{
try
{
array[arrayIndex + i] = this[i];
}
catch (Exception)
{
throw new ArgumentNullException();
}
}
}
//UnitTest
public int IndexOf(T item)
{
var index = 0;
for (var iNode = _head; iNode != null; iNode = iNode.next, index++)
{
if (TEquals(item, iNode))
{
return index;
}
}
return -1;
}
//UnitTest
public void Insert(int index, T item)
{
OnChanged();
var itemNode = new Node(item);
if (index == 0)
{
itemNode.next = _head;
_head = itemNode;
Count++;
return;
}
CheckIndex(index - 1);
var indexNode = SearchIndex(index - 1);
itemNode.next = indexNode.next;
indexNode.next = itemNode;
Count++;
}
private Node SearchIndex(int index)
{
CheckIndex(index);
var result = _head == null ? throw new ArgumentNullException() : _head;
for (int i = 0; i < index; i++)
{
result = result.next;
}
return result;
}
private void CheckIndex(int index)
{
if (index < 0 || index >= Count)
{
throw new ArgumentNullException();
}
}
//UnitTest
public bool Remove(T item)
{
OnChanged();
T oldData;
var precursor = SearchPrecursor(item);
if (precursor == null)
{
return false;
}
if (precursor == _head && TEquals(item, precursor))
{
oldData = _head.data;
_head = _head.next;
Count--;
return true;
}
oldData = precursor.next.data;
precursor.next = precursor.next.next;
Count--;
return true;
}
private bool TEquals(T item, Node node)
{
return ((node.data != null && node.data.Equals(item)) || (node.data == null && item == null));
}
private Node SearchPrecursor(T item)
{
var precursor = _head;
if (TEquals(item, precursor))
{
return _head;
}
while (precursor.next != null)
{
if (TEquals(item, precursor.next))
{
return precursor;
}
precursor = precursor.next;
}
return null;
}
public void RemoveAt(int index)
{
OnChanged();
T oldData;
var removeTarget = this[index];
var precursor = SearchPrecursor(removeTarget);
if (precursor == null)
{
return;
}
if (precursor == _head && TEquals(removeTarget, precursor))
{
oldData = _head.data;
_head = _head.next;
}
oldData = precursor.next.data;
precursor.next = precursor.next.next;
Count--;
}
private void Foreach(Action<Node> ac)
{
for (var iNode = _head; iNode != null; iNode = iNode.next)
{
ac(iNode);
}
}
public void Display()
{
Foreach((Node iNode) => { Console.Write("{0} ", iNode.data); });
}
}
另外我这里这给出一个用yield return思路实现的代码:
public class MyLinkedList<T> : IList<T>
{
class Node
{
public T data;
public Node next;
public Node(T data)
{
this.data = data;
}
}
private Node _head;
public MyLinkedList()
{
_head = null;
}
//获取和设置指定处的元素
public T this[int index]
{
get
{
return SearchIndex(index).data;
}
set
{
SearchIndex(index).data = value;
OnChanged();
}
}
//获取当前链表中元素的个数
public int Count { get; set; }
public bool IsReadOnly { get { return false; } }
private bool _changed = false;
private void OnChanged() =>
Changed?.Invoke(this, EventArgs.Empty);
private event EventHandler Changed;
private void OnChanged(object sender, EventArgs e)
{
_changed = true;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
//UnitTest
public IEnumerator<T> GetEnumerator()
{
Changed += OnChanged;
for (int i = 0; i < Count; i++)
{
if (_changed)
{
_changed = false;
throw new InvalidOperationException();
}
yield return this[i];
}
Changed -= OnChanged;
}
//UnitTest
public void Add(T item)
{
var itemNode = new Node(item);
if (_head == null)
{
_head = itemNode;
}
else
{
var iNode = _head;
for (; iNode.next != null; iNode = iNode.next)
{
}
iNode.next = itemNode;
}
Count++;
OnChanged();
}
public void Clear()
{
_head = null;
Count = 0;
OnChanged();
}
public bool Contains(T item)
{
return IndexOf(item) != -1;
}
public void CopyTo(T[] array, int arrayIndex)
{
if (array == null)
{
throw new ArgumentNullException();
}
try
{
for (int i = 0; i < Count; i++)
{
array[arrayIndex + i] = this[i];
}
}
catch (Exception)
{
throw new IndexOutOfRangeException();
}
}
//UnitTest
public int IndexOf(T item)
{
var index = 0;
for (var iNode = _head; iNode != null; iNode = iNode.next, index++)
{
if (TEquals(item, iNode))
{
return index;
}
}
return -1;
}
//UnitTest
public void Insert(int index, T item)
{
var itemNode = new Node(item);
if (index == 0)
{
itemNode.next = _head;
_head = itemNode;
Count++;
return;
}
CheckIndex(index - 1);
var indexNode = SearchIndex(index - 1);
itemNode.next = indexNode.next;
indexNode.next = itemNode;
Count++;
OnChanged();
}
private Node SearchIndex(int index)
{
CheckIndex(index);
var result = _head == null ? throw new ArgumentNullException() : _head;
for (int i = 0; i < index; i++)
{
result = result.next;
}
return result;
}
private void CheckIndex(int index)
{
if (index < 0 || index >= Count)
{
throw new IndexOutOfRangeException();
}
}
//UnitTest
public bool Remove(T item)
{
T oldData;
var precursor = SearchPrecursor(item);
if (precursor == null)
{
OnChanged();
return false;
}
if (precursor == _head && TEquals(item, precursor))
{
oldData = _head.data;
_head = _head.next;
Count--;
OnChanged();
return true;
}
oldData = precursor.next.data;
precursor.next = precursor.next.next;
Count--;
OnChanged();
return true;
}
private bool TEquals(T item, Node node)
{
return ((node.data != null && node.data.Equals(item)) || (node.data == null && item == null));
}
private Node SearchPrecursor(T item)
{
var precursor = _head;
if (TEquals(item, precursor))
{
return _head;
}
while (precursor.next != null)
{
if (TEquals(item, precursor.next))
{
return precursor;
}
precursor = precursor.next;
}
return null;
}
public void RemoveAt(int index)
{
T oldData;
var removeTarget = this[index];
var precursor = SearchPrecursor(removeTarget);
if (precursor == null)
{
OnChanged();
return;
}
if (precursor == _head && TEquals(removeTarget, precursor))
{
oldData = _head.data;
_head = _head.next;
}
oldData = precursor.next.data;
precursor.next = precursor.next.next;
Count--;
OnChanged();
}
private void Foreach(Action<Node> ac)
{
for (var iNode = _head; iNode != null; iNode = iNode.next)
{
ac(iNode);
}
}
public void Display()
{
Foreach((Node iNode) => { Console.Write("{0} ", iNode.data); });
}
}