Show / Hide Table of Contents

Interface IScopeAwareEnumerable<T>

Interface for scope aware enumerable. Some methods may not consume enumerable immediately and may want to save enumerable reference. In that case if enumerable isn't scoped then it may just save reference to enumerable otherwise it need to make a copy, because enumerable is only valid for the scope (i.e. it may be modified when leaves scope or use inside LINQ references to objects which may become invalid after leaving scope - disposed or replaced). This approach helps to safely pass enumerable to such methods with explicit scope control. Usually you should either assume enumerable unscoped and just save reference to it, in other cases it may be only valid in the scope and you need to copy it. You must follow rules to avoid problems and errors and most safe way is to always copy, but it is an expensive approach. But using IScopeAwareEnumerable<T> you may explicitly say if enumerable is only valid within a scope and then decide if you need to copy it or not when want to save reference. This pattern inspired by C# 11 scoped keyword for references (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/method-parameters#scope-of-references-and-values).

var l = new List<string> { "Hello", "World" };
Call(l.Unscoped()); // error, because l modified outside of scope (Clear call bellow) and can't be considered valid if leaves scope inside Call method
Call(l.Scoped());   // ok, if l have to leave scope inside of Call then it will be a copy of original list and won't be impacted by Clear call bellow
l.Clear();
Call(new List<string> { "Hello", "World" }.Unscoped());             // ok, because you don't have other references to List and it guaranteed to not be modified
Call(Enumerable.Range(1, 10).Select(i => i.ToString()).Unscoped()); // ok, because this enumerable doesn't depend on any shared state and may be safely resolved outside of scope
var db = new DB();
Call(Enumerable.Range(1, 10).Select(i => db.GetString(i)).Unscoped()); // error, because this enumerable depends on db which then disposed and if enumerable leaves scope of Call method and we will try to use it later then it will fail with error trying to access db object
db.Dispose();

void Call(IScopedEnumerable<string> enumerable) { IEnumerable<string> unscoped = enumerable.IsScoped ? enumerable.ToUnscopedCollection() : enumerable; Task.Run(() => foreach (var item in unscoped) { Console.WriteLine(item); }); }

>

Namespace: Eco.Shared.Collections
Assembly: Eco.Shared.dll
Syntax
public interface IScopeAwareEnumerable<out T> : IEnumerable<T>, IEnumerable
Type Parameters
Name Description
T

Properties

IsScoped

Declaration
bool IsScoped { get; }
Property Value
Type Description
System.Boolean

Methods

ToUnscopedCollection()

Returns unscoped collection which may be safely used outside of local scope. Makes defensive copy for scoped enumerable.

Declaration
IReadOnlyCollection<T> ToUnscopedCollection()
Returns
Type Description
System.Collections.Generic.IReadOnlyCollection<T>

Extension Methods

CommandLine.FeedFromCommandLine(Object)
CommandLine.ToCommandLineArgs(Object, Func<Object, Boolean>)
ListUtil.DepthFirstTraversal<T>(T, Func<T, IEnumerable<T>>)
EnumerableExtensions.SingleItemAsEnumerable<T>(T)
EventUtils.RaiseEvent<TEventArgs>(Object, String, TEventArgs)
PredicateUtils.MatchesAll<TEnumerable, T>(T, TEnumerable)
PredicateUtils.MatchesAll<T>(T, Func<T, Boolean>[])
PredicateUtils.MatchesAny<TEnumerable, T>(T, TEnumerable)
ReflectionUtils.PropertyValue<T>(Object, PropertyInfo)
ReflectionUtils.TryGetPropertyValueByName<T>(Object, String, out T)
ReflectionUtils.GetPropertyValueByName<T>(Object, String)
ReflectionUtils.SetPropertyByName(Object, String, Object)
ReflectionUtils.GetStructPropertyByName<T>(Object, String)
ReflectionUtils.GetStringPropertyByName(Object, String)
ReflectionUtils.ZipByProperty<T>(Object, Object, Object, Func<T, T, T>)
☀
☾
In This Article
Back to top
Copyright (c) Strange Loop Games 2021
☀
☾