Indexer Impersonation¶
Creating an imposter¶
Use the generated imposter type for your indexer-bearing interface or class and obtain the configured instance:
Example
[assembly: GenerateImposter(typeof(IMyServiceSut))]
public interface IMyServiceSut
{
int this[int key1] { get; set; }
}
Getter¶
Example
var imposter = IMyServiceSut.Imposter();
imposter[Arg<int>.Is(k => k > 0)].Getter().Returns(10);
var service = imposter.Instance();
var value = service[123]; // 10
When getter setups overlap, the newest matching setup with a configured return or throw wins. UseBaseImplementation also counts as a configured return. A newer getter builder or callback-only setup does not hide an older configured return, including while another thread is configuring it. If no matching setup has a configured return, the usual default or missing-setup behavior applies.
Setter¶
Example
// Observe writes
imposter[Arg<int>.Any()].Setter().Callback((key, value) =>
{
// value is 50
});
var service = imposter.Instance();
service[42] = 50;
Span keys and values¶
An indexer whose keys or value are Span<T> or ReadOnlySpan<T> can be impersonated. The imposter keeps each span as the array of its elements:
- Match a span key with
SpanArg<T>orReadOnlySpanArg<T>(see Span parameters). Delegates passed toReturnsandCallbackget a copy of the key's elements as an array.
Example
// int this[ReadOnlySpan<char> key] { get; set; }
imposter[ReadOnlySpanArg<char>.Is('a', 'b')].Getter().Returns(3);
var value = imposter.Instance()["ab".AsSpan()]; // 3
- Without a setup, keys that hold the same elements address the same value.
Example
imposter.Instance()["ab".AsSpan()] = 5;
var value = imposter.Instance()[new[] { 'a', 'b' }]; // 5
- A span value works as it does for span properties:
Getter().Returnstakes the array the returned span covers, and the imposter keeps a copy of the elements a span value is set to.
Ref struct values¶
An indexer whose value is another ref struct can be impersonated too. The imposter can't keep or match the value, so it only passes it between the indexer and your delegates. Keys work as usual:
Getter().Returnstakes only the delegate that gets the keys; there's noReturns(value)orReturns(() => value).Throws, callbacks,Then()sequences andUseBaseImplementation()work as usual.
Example
// Marker this[int index] { get; set; }, where Marker is a ref struct
imposter[Arg<int>.Any()].Getter().Returns(index => new Marker(index + 1));
var position = imposter.Instance()[2].Position; // 3
- Setter callbacks get the keys and the value, and
Calledcounts the sets by their keys.
Example
var sets = new List<(int, int)>();
imposter[Arg<int>.Any()]
.Setter()
.Callback((index, value) => sets.Add((index, value.Position)));
imposter.Instance()[3] = new Marker(4); // sets is { (3, 4) }
- Without a setup, the getter returns what the base getter returns, for a class indexer that has one, or the default otherwise. Setting the indexer doesn't change what it returns.
A class indexer with a key taken by in or ref readonly and a getter with a base implementation still reports IMP009: its base getter reads the base indexer through a copy of the key, which the value could refer to.
Ref struct keys¶
A key of another ref struct type can't be kept or matched either, so setups and verification match the other keys only, as a method's ref struct parameters are. The delegates passed to Returns, Callback and Throws, and the base implementation, still get the key:
Example
// int this[int row, Locator locator] { get; set; }, where Locator is a ref struct
imposter[Arg<int>.Any()].Getter().Returns((row, locator) => row + locator.Offset);
var value = imposter.Instance()[2, new Locator(3)]; // 5
The default behaviour keeps a value set by the other keys, whatever the ref struct key.
An indexer whose keys are all ref structs has no keys left to match, and two indexers whose other keys are the same would share one setup indexer. Such an indexer is set up by a method named after it instead: Indexer(), or Indexer_1() and so on when the target has more than one indexer. Its interface's setup view declares the same method.
Example
// string this[Locator locator] { get; set; }, the target's second indexer
imposter.Indexer_1().Getter().Returns(locator => locator.Offset.ToString());
var value = imposter.Instance()[new Locator(8)]; // "8"
A ref struct key taken by in still reports IMP009.