Skip to content

Commit 44623f6

Browse files
committed
Use ref struct as disposable instance to avoid allocation. Also avoids closure allocation
1 parent d2703e1 commit 44623f6

File tree

3 files changed

+69
-49
lines changed

3 files changed

+69
-49
lines changed

Orm/Xtensive.Orm/Collections/BindingCollection.cs

Lines changed: 61 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using System.Collections;
99
using System.Collections.Generic;
1010
using System.Diagnostics;
11-
using Xtensive.Core;
1211

1312

1413
namespace Xtensive.Collections
@@ -21,8 +20,50 @@ namespace Xtensive.Collections
2120
/// </summary>
2221
[Serializable]
2322
[DebuggerDisplay("Count = {Count}")]
24-
public class BindingCollection<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
23+
public class BindingCollection<TKey, TValue> : IReadOnlyCollection<KeyValuePair<TKey, TValue>>
2524
{
25+
public readonly ref struct BindingScope
26+
{
27+
public static BindingScope Empty => new BindingScope();
28+
29+
private readonly BindingCollection<TKey, TValue> owner;
30+
private readonly TKey key;
31+
private readonly TValue prevValue;
32+
private readonly bool prevValueExists;
33+
34+
public void Dispose()
35+
{
36+
if (owner == null) {
37+
return;
38+
}
39+
40+
if (prevValueExists) {
41+
if (!owner.permanentBindings.Contains(key)) {
42+
owner.bindings[key] = prevValue;
43+
}
44+
}
45+
else {
46+
if (!owner.permanentBindings.Contains(key)) {
47+
owner.bindings.Remove(key);
48+
}
49+
}
50+
}
51+
52+
public BindingScope(BindingCollection<TKey, TValue> owner, TKey key) : this()
53+
{
54+
this.owner = owner;
55+
this.key = key;
56+
}
57+
58+
public BindingScope(BindingCollection<TKey, TValue> owner, TKey key, TValue prevValue)
59+
{
60+
this.owner = owner;
61+
this.key = key;
62+
this.prevValue = prevValue;
63+
prevValueExists = true;
64+
}
65+
}
66+
2667
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
2768
private readonly Dictionary<TKey, TValue> bindings = new Dictionary<TKey, TValue>();
2869
private readonly HashSet<TKey> permanentBindings = new HashSet<TKey>();
@@ -32,7 +73,7 @@ public class BindingCollection<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TV
3273
/// </summary>
3374
public virtual int Count {
3475
[DebuggerStepThrough]
35-
get { return bindings.Count; }
76+
get => bindings.Count;
3677
}
3778

3879
/// <summary>
@@ -41,7 +82,7 @@ public virtual int Count {
4182
/// <value></value>
4283
public virtual TValue this[TKey key] {
4384
[DebuggerStepThrough]
44-
get { return bindings[key]; }
85+
get => bindings[key];
4586
}
4687

4788
/// <summary>
@@ -51,24 +92,15 @@ public virtual TValue this[TKey key] {
5192
/// <param name="value">The value to bind.</param>
5293
/// <returns>Disposable object that will
5394
/// destroy the binding on its disposal.</returns>
54-
public virtual IDisposable Add(TKey key, TValue value)
95+
public virtual BindingScope Add(TKey key, TValue value)
5596
{
56-
TValue previous;
57-
58-
if (bindings.TryGetValue(key, out previous)) {
97+
if (bindings.TryGetValue(key, out var previous)) {
5998
bindings[key] = value;
60-
return new Disposable(isDisposing => {
61-
if (!permanentBindings.Contains(key))
62-
bindings[key] = previous;
63-
});
64-
}
65-
else {
66-
bindings.Add(key, value);
67-
return new Disposable(isDisposing => {
68-
if (!permanentBindings.Contains(key))
69-
bindings.Remove(key);
70-
});
99+
return new BindingScope(this, key, previous);
71100
}
101+
102+
bindings.Add(key, value);
103+
return new BindingScope(this, key);
72104
}
73105

74106
/// <summary>
@@ -80,8 +112,9 @@ public virtual IDisposable Add(TKey key, TValue value)
80112
public virtual void PermanentAdd(TKey key, TValue value)
81113
{
82114
bindings[key] = value;
83-
if (!permanentBindings.Contains(key))
115+
if (!permanentBindings.Contains(key)) {
84116
permanentBindings.Add(key);
117+
}
85118
}
86119

87120
/// <summary>
@@ -92,8 +125,10 @@ public virtual void PermanentAdd(TKey key, TValue value)
92125
/// <exception cref="KeyNotFoundException">Key isn't found.</exception>
93126
public virtual void ReplaceBound(TKey key, TValue value)
94127
{
95-
if (!bindings.ContainsKey(key))
128+
if (!bindings.ContainsKey(key)) {
96129
throw new KeyNotFoundException();
130+
}
131+
97132
bindings[key] = value;
98133
}
99134

@@ -108,45 +143,27 @@ public virtual void ReplaceBound(TKey key, TValue value)
108143
/// contains an element with the specified key;
109144
/// otherwise, <see langword="false" />.</returns>
110145
[DebuggerStepThrough]
111-
public virtual bool TryGetValue(TKey key, out TValue value)
112-
{
113-
return bindings.TryGetValue(key, out value);
114-
}
146+
public virtual bool TryGetValue(TKey key, out TValue value) => bindings.TryGetValue(key, out value);
115147

116148
/// <summary>
117149
/// Gets the sequence of bound keys.
118150
/// </summary>
119151
/// <returns>The sequence of bound keys.</returns>
120152
public virtual IEnumerable GetKeys()
121153
{
122-
foreach (var key in bindings.Keys)
154+
foreach (var key in bindings.Keys) {
123155
yield return key;
156+
}
124157
}
125158

126159
#region IEnumerable<...> methods
127160

128161
/// <inheritdoc/>
129-
public virtual IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
130-
{
131-
return bindings.GetEnumerator();
132-
}
162+
public virtual IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => bindings.GetEnumerator();
133163

134164
/// <inheritdoc/>
135-
IEnumerator IEnumerable.GetEnumerator()
136-
{
137-
return GetEnumerator();
138-
}
165+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
139166

140167
#endregion
141-
142-
143-
// Constructors
144-
145-
/// <summary>
146-
/// Initializes new instance of this type.
147-
/// </summary>
148-
public BindingCollection()
149-
{
150-
}
151168
}
152169
}

Orm/Xtensive.Orm/Orm/Linq/LinqBindingCollection.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ internal class LinqBindingCollection : BindingCollection<ParameterExpression, Pr
1919
private readonly Dictionary<ParameterExpression, IEnumerable<ParameterExpression>> linkedParameters
2020
= new Dictionary<ParameterExpression, IEnumerable<ParameterExpression>>();
2121

22-
public override IDisposable Add(ParameterExpression key, ProjectionExpression value)
22+
public override BindingScope Add(ParameterExpression key, ProjectionExpression value)
2323
{
24-
if (!key.Type.IsAssignableFrom(value.ItemProjector.Type))
25-
throw new ArgumentException(Strings.ExParameterExpressionMustHaveSameTypeAsProjectionExpressionItemProjector, "key");
24+
if (!key.Type.IsAssignableFrom(value.ItemProjector.Type)) {
25+
throw new ArgumentException(
26+
Strings.ExParameterExpressionMustHaveSameTypeAsProjectionExpressionItemProjector, nameof(key));
27+
}
28+
2629
return base.Add(key, value);
2730
}
2831

Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,7 +1072,7 @@ private ProjectionExpression VisitSelectMany(Expression source, LambdaExpression
10721072
var visitedSource = Visit(source);
10731073
var sequence = VisitSequence(visitedSource);
10741074

1075-
IDisposable indexBinding = null;
1075+
var indexBinding = BindingCollection<ParameterExpression, ProjectionExpression>.BindingScope.Empty;
10761076
if (collectionSelector.Parameters.Count==2) {
10771077
var indexProjection = GetIndexBinding(collectionSelector, ref sequence);
10781078
indexBinding = context.Bindings.Add(collectionSelector.Parameters[1], indexProjection);
@@ -1191,7 +1191,7 @@ private ProjectionExpression VisitWhere(Expression expression, LambdaExpression
11911191
{
11921192
var parameter = le.Parameters[0];
11931193
ProjectionExpression visitedSource = VisitSequence(expression);
1194-
IDisposable indexBinding = null;
1194+
var indexBinding = BindingCollection<ParameterExpression, ProjectionExpression>.BindingScope.Empty;
11951195
if (le.Parameters.Count==2) {
11961196
var indexProjection = GetIndexBinding(le, ref visitedSource);
11971197
indexBinding = context.Bindings.Add(le.Parameters[1], indexProjection);

0 commit comments

Comments
 (0)