ObjectOwner<T> Struct
A RAII (Resource Acquisition Is Initialization) wrapper for objects obtained from ObjectPool<T>.
Namespace: CryptoHives.Foundation.Memory.Pools · API reference
Overview
The constructor rents an object from the pool, PooledObject exposes it, and Dispose gives it back. Declare it with using var and keep it local: it is a struct, so casting it to IDisposable boxes it, and a copy that is also disposed returns the same object to the pool twice.
Usage Examples
Basic Usage
using Microsoft.Extensions.ObjectPool;
var pool = new DefaultObjectPool<StringBuilder>(
new DefaultPooledObjectPolicy<StringBuilder>()
);
using var owner = new ObjectOwner<StringBuilder>(pool);
StringBuilder sb = owner.PooledObject;
sb.Append("Hello");
sb.Append(" World");
Console.WriteLine(sb.ToString());
// StringBuilder automatically returned to pool when owner disposes
With Custom Pool Policy
public class StringBuilderPolicy : IPooledObjectPolicy<StringBuilder>
{
public StringBuilder Create() => new StringBuilder();
public bool Return(StringBuilder obj)
{
obj.Clear();
return true;
}
}
var pool = new DefaultObjectPool<StringBuilder>(new StringBuilderPolicy());
using var owner = new ObjectOwner<StringBuilder>(pool);
StringBuilder sb = owner.PooledObject;
// Use sb...
Processing Collections
public List<int> ProcessData(IEnumerable<int> items)
{
var pool = new DefaultObjectPool<List<int>>(
new ListPolicy<int>()
);
using var owner = new ObjectOwner<List<int>>(pool);
List<int> list = owner.PooledObject;
foreach (int item in items)
{
if (item > 0)
{
list.Add(item);
}
}
return new List<int>(list); // Return a copy, not the pooled list
}
Thread Safety
Thread-safe when used correctly (one owner per instance). The pool itself handles thread-safety.
Best Practices
Use with using statement
using var owner = new ObjectOwner<StringBuilder>(pool);
StringBuilder sb = owner.PooledObject;
// Use sb...
// Automatically returned to pool
Keep scope minimal
string result;
using (var owner = new ObjectOwner<StringBuilder>(pool))
{
StringBuilder sb = owner.PooledObject;
sb.Append("Data");
result = sb.ToString();
} // Pool object returned here
return result;
Don't store in fields
// Wrong!
public class BadExample
{
private ObjectOwner<StringBuilder> _owner; // Don't do this!
}
Don't return the pooled object
// Wrong!
public StringBuilder GetStringBuilder()
{
using var owner = new ObjectOwner<StringBuilder>(pool);
return owner.PooledObject; // Object will be returned to pool!
}
// Correct: Return the result
public string GetString()
{
using var owner = new ObjectOwner<StringBuilder>(pool);
return owner.PooledObject.ToString();
}
Don't box the struct
// Wrong!
IDisposable disposable = new ObjectOwner<StringBuilder>(pool); // Boxing!
// Correct:
using var owner = new ObjectOwner<StringBuilder>(pool);
Don't copy the struct
// Wrong!
var owner1 = new ObjectOwner<StringBuilder>(pool);
var owner2 = owner1; // Copy! Both will try to return to pool
owner1.Dispose();
owner2.Dispose(); // Double return!
// Correct: Use once
using var owner = new ObjectOwner<StringBuilder>(pool);
Common Use Cases
Temporary String Building
public string FormatData(params object[] values)
{
using var owner = new ObjectOwner<StringBuilder>(stringBuilderPool);
StringBuilder sb = owner.PooledObject;
foreach (var value in values)
{
sb.Append(value);
sb.Append(' ');
}
return sb.ToString().TrimEnd();
}
Temporary Collections
public int[] FilterPositive(int[] values)
{
using var owner = new ObjectOwner<List<int>>(listPool);
List<int> list = owner.PooledObject;
foreach (int value in values)
{
if (value > 0)
{
list.Add(value);
}
}
return list.ToArray();
}
Temporary Buffers
public byte[] ProcessData(byte[] input)
{
using var owner = new ObjectOwner<MemoryStream>(streamPool);
MemoryStream stream = owner.PooledObject;
stream.Write(input, 0, input.Length);
// Process stream...
return stream.ToArray();
}
Performance Characteristics
- Construction: O(1) - gets object from pool
- Disposal: O(1) - returns object to pool
- Memory: No heap allocation (struct, no boxing)
Remarks
Why a Struct?
ObjectOwner<T> is a struct to avoid heap allocation. When used correctly with using, it provides zero-allocation resource management.
Comparison with Direct Pool Usage
// Without ObjectOwner (error-prone)
var obj = pool.Get();
try
{
// Use obj...
}
finally
{
pool.Return(obj);
}
// With ObjectOwner (safer, cleaner)
using var owner = new ObjectOwner<T>(pool);
T obj = owner.PooledObject;
// Use obj...
See Also
© 2025 The Keepers of the CryptoHives