Skip to content

Commit ca7fe23

Browse files
committed
Util/JoinedEnumerable.cs: Split the IEnumerator implementation to a nested class. This is so that the JoinedEnumerable as a whole doesn't require the wrapped enumerators to implement Reset()
1 parent d45a965 commit ca7fe23

File tree

1 file changed

+118
-104
lines changed

1 file changed

+118
-104
lines changed

src/NHibernate/Util/JoinedEnumerable.cs

Lines changed: 118 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,168 +1,182 @@
11
using System;
2+
using System.Linq;
23
using System.Collections;
3-
44
using System.Collections.Generic;
55

66
namespace NHibernate.Util
77
{
88
/// <summary>
9-
/// Combines multiple objects implementing <see cref="IEnumerable"/> into one.
9+
/// Concatenates multiple objects implementing <see cref="IEnumerable"/> into one.
1010
/// </summary>
11-
public class JoinedEnumerable : IEnumerable, IEnumerator, IDisposable
11+
public class JoinedEnumerable : IEnumerable
1212
{
1313
private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof(JoinedEnumerable));
1414

15-
private readonly IEnumerator[] _enumerators;
16-
private int _current;
15+
private readonly IEnumerable[] _enumerables;
1716

1817
/// <summary>
1918
/// Creates an IEnumerable object from multiple IEnumerables.
2019
/// </summary>
2120
/// <param name="enumerables">The IEnumerables to join together.</param>
2221
public JoinedEnumerable(IEnumerable[] enumerables)
2322
{
24-
_enumerators = new IEnumerator[enumerables.Length];
25-
for (int i = 0; i < enumerables.Length; i++)
26-
{
27-
_enumerators[i] = enumerables[i].GetEnumerator();
28-
}
29-
_current = 0;
23+
_enumerables = new IEnumerable[enumerables.Length];
24+
Array.Copy(enumerables, _enumerables, enumerables.Length);
3025
}
3126

32-
public JoinedEnumerable(List<IEnumerable> enumerables)
27+
public JoinedEnumerable(IEnumerable<IEnumerable> enumerables)
3328
: this(enumerables.ToArray())
3429
{
3530
}
3631

3732
public JoinedEnumerable(IEnumerable first, IEnumerable second)
38-
: this(new IEnumerable[] { first, second })
33+
: this(new[] { first, second })
3934
{
4035
}
4136

4237

43-
#region System.Collections.IEnumerator Members
38+
#region System.Collections.IEnumerable Members
4439

4540
/// <summary></summary>
46-
public bool MoveNext()
41+
public IEnumerator GetEnumerator()
4742
{
48-
for (; _current < _enumerators.Length; _current++)
49-
{
50-
if (_enumerators[_current].MoveNext())
51-
{
52-
return true;
53-
}
54-
else
55-
{
56-
// there are no items left to iterate over in the current
57-
// enumerator so go ahead and dispose of it.
58-
IDisposable disposable = _enumerators[_current] as IDisposable;
59-
if (disposable != null)
60-
{
61-
disposable.Dispose();
62-
}
63-
}
64-
}
65-
return false;
43+
var enumerators = _enumerables.Select(enumerable => enumerable.GetEnumerator());
44+
return new JoinedEnumerator(enumerators);
6645
}
6746

68-
/// <summary></summary>
69-
public void Reset()
47+
#endregion
48+
49+
50+
51+
#region Nested class JoinedEnumerator
52+
53+
private class JoinedEnumerator : IEnumerator, IDisposable
7054
{
71-
for (int i = 0; i < _enumerators.Length; i++)
55+
private readonly IEnumerator[] _enumerators;
56+
private int _current;
57+
58+
public JoinedEnumerator(IEnumerable<IEnumerator> enumerators)
7259
{
73-
_enumerators[i].Reset();
60+
_enumerators = enumerators.ToArray();
61+
_current = 0;
7462
}
75-
_current = 0;
76-
}
7763

78-
/// <summary></summary>
79-
public object Current
80-
{
81-
get { return _enumerators[_current].Current; }
82-
}
8364

84-
#endregion
65+
#region System.Collections.IEnumerator Members
8566

86-
#region System.Collections.IEnumerable Members
67+
public bool MoveNext()
68+
{
69+
for (; _current < _enumerators.Length; _current++)
70+
{
71+
if (_enumerators[_current].MoveNext())
72+
{
73+
return true;
74+
}
75+
else
76+
{
77+
// there are no items left to iterate over in the current
78+
// enumerator so go ahead and dispose of it.
79+
IDisposable disposable = _enumerators[_current] as IDisposable;
80+
if (disposable != null)
81+
{
82+
disposable.Dispose();
83+
}
84+
}
85+
}
86+
return false;
87+
}
8788

88-
/// <summary></summary>
89-
public IEnumerator GetEnumerator()
90-
{
91-
Reset();
92-
return this;
93-
}
9489

95-
#endregion
90+
public void Reset()
91+
{
92+
foreach (var enumerator in _enumerators)
93+
enumerator.Reset();
94+
_current = 0;
95+
}
9696

97-
#region IDisposable Members
9897

99-
/// <summary>
100-
/// A flag to indicate if <c>Dispose()</c> has been called.
101-
/// </summary>
102-
private bool _isAlreadyDisposed;
98+
public object Current
99+
{
100+
get { return _enumerators[_current].Current; }
101+
}
103102

104-
/// <summary>
105-
/// Finalizer that ensures the object is correctly disposed of.
106-
/// </summary>
107-
~JoinedEnumerable()
108-
{
109-
Dispose(false);
110-
}
103+
#endregion
111104

112-
/// <summary>
113-
/// Takes care of freeing the managed and unmanaged resources that
114-
/// this class is responsible for.
115-
/// </summary>
116-
public void Dispose()
117-
{
118-
log.Debug("running JoinedEnumerable.Dispose()");
119-
Dispose(true);
120-
}
105+
#region IDisposable Members
121106

107+
/// <summary>
108+
/// A flag to indicate if <c>Dispose()</c> has been called.
109+
/// </summary>
110+
private bool _isAlreadyDisposed;
122111

123-
/// <summary>
124-
/// Takes care of freeing the managed and unmanaged resources that
125-
/// this class is responsible for.
126-
/// </summary>
127-
/// <param name="isDisposing">Indicates if this JoinedEnumerable is being Disposed of or Finalized.</param>
128-
/// <remarks>
129-
/// The command is closed and the reader is disposed. This allows other ADO.NET
130-
/// related actions to occur without needing to move all the way through the
131-
/// EnumerableImpl.
132-
/// </remarks>
133-
protected virtual void Dispose(bool isDisposing)
134-
{
135-
if (_isAlreadyDisposed)
112+
/// <summary>
113+
/// Finalizer that ensures the object is correctly disposed of.
114+
/// </summary>
115+
~JoinedEnumerator()
136116
{
137-
// don't dispose of multiple times.
138-
return;
117+
Dispose(false);
139118
}
140119

141-
// free managed resources that are being managed by the JoinedEnumerable if we
142-
// know this call came through Dispose()
143-
if (isDisposing)
120+
/// <summary>
121+
/// Takes care of freeing the managed and unmanaged resources that
122+
/// this class is responsible for.
123+
/// </summary>
124+
public void Dispose()
144125
{
145-
// dispose each IEnumerable that still needs to be disposed of
146-
for (; _current < _enumerators.Length; _current++)
126+
log.Debug("running JoinedEnumerator.Dispose()");
127+
Dispose(true);
128+
}
129+
130+
131+
/// <summary>
132+
/// Takes care of freeing the managed and unmanaged resources that
133+
/// this class is responsible for.
134+
/// </summary>
135+
/// <param name="isDisposing">Indicates if this JoinedEnumerable is being Disposed of or Finalized.</param>
136+
/// <remarks>
137+
/// The command is closed and the reader is disposed. This allows other ADO.NET
138+
/// related actions to occur without needing to move all the way through the
139+
/// EnumerableImpl.
140+
/// </remarks>
141+
private void Dispose(bool isDisposing)
142+
{
143+
if (_isAlreadyDisposed)
147144
{
148-
IDisposable currentDisposable = _enumerators[_current] as IDisposable;
149-
if (currentDisposable != null)
145+
// don't dispose of multiple times.
146+
return;
147+
}
148+
149+
// free managed resources that are being managed by the JoinedEnumerable if we
150+
// know this call came through Dispose()
151+
if (isDisposing)
152+
{
153+
// dispose each IEnumerable that still needs to be disposed of
154+
for (; _current < _enumerators.Length; _current++)
150155
{
151-
currentDisposable.Dispose();
156+
IDisposable currentDisposable = _enumerators[_current] as IDisposable;
157+
if (currentDisposable != null)
158+
{
159+
currentDisposable.Dispose();
160+
}
152161
}
153162
}
154-
}
155163

156-
// free unmanaged resources here
164+
// free unmanaged resources here
165+
166+
_isAlreadyDisposed = true;
167+
// nothing for Finalizer to do - so tell the GC to ignore it
168+
GC.SuppressFinalize(this);
169+
}
157170

158-
_isAlreadyDisposed = true;
159-
// nothing for Finalizer to do - so tell the GC to ignore it
160-
GC.SuppressFinalize(this);
171+
#endregion
161172
}
162173

163174
#endregion
164175
}
165176

177+
178+
179+
166180
public class JoinedEnumerable<T> : IEnumerable<T>
167181
{
168182
private readonly IEnumerable<T>[] enumerables;
@@ -195,7 +209,7 @@ IEnumerator<T> IEnumerable<T>.GetEnumerator()
195209

196210
public IEnumerator GetEnumerator()
197211
{
198-
return ((IEnumerable<T>) this).GetEnumerator();
212+
return ((IEnumerable<T>)this).GetEnumerator();
199213
}
200214

201215
#endregion
@@ -273,7 +287,7 @@ public void Reset()
273287

274288
public object Current
275289
{
276-
get { return ((IEnumerator<T>) this).Current; }
290+
get { return ((IEnumerator<T>)this).Current; }
277291
}
278292

279293
#endregion

0 commit comments

Comments
 (0)