Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion FrameworkFeatureConstants.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<DefineConstants>$(DefineConstants);INDEX_OF_CHAR_COMPARISONTYPE_SUPPORTED;TIMESPAN_MULTIPLY_SUPPORTED;SPLIT_ACCEPTS_STRING_SEPARATOR;LAZY_RETURN_CONSTRUCTOR;QUEUE_TRY_OVERLOADS;OPTIMIZED_ELEMENT_AT;RANGE_SUPPORTED;READ_ONLY_SPAN_SUPPORTED;INDEX_TYPE;JOIN_TO_STRING_CHAR_SEPARATOR;REMOVE_EXTENSION</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'netcoreapp3.0'))">
<DefineConstants>$(DefineConstants);SYSTEM_INDEX_SUPPORTED;IP_END_POINT_TRY_PARSE_SUPPORTED</DefineConstants>
<DefineConstants>$(DefineConstants);SYSTEM_INDEX_SUPPORTED;IP_END_POINT_TRY_PARSE_SUPPORTED;OPTIMIZED_FIRST_LAST_OR_DEFAULT</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net5.0'))">
<DefineConstants>$(DefineConstants);SET_CURRENT_STACK_TRACE_SUPPORTED;DYNAMICALLY_ACCESSED_MEMBERS_ATTRIBUTE_SUPPORTED</DefineConstants>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma warning disable SA1010 // StyleCop support for collection expressions is missing
using System.Collections;
using Funcky.Test.TestUtils;
using Xunit.Sdk;

namespace Funcky.Test.Extensions.EnumerableExtensions;

Expand All @@ -24,6 +26,21 @@ public void GivenAnEnumerableSingleOrNoneGivesTheCorrectOption(List<int> valueEn
ExpectedSingleOrNoneBehaviour(valueEnumerable, () => referenceEnumerable.SingleOrNone().Match(none: false, some: True));
}

#if OPTIMIZED_FIRST_LAST_OR_DEFAULT
[Fact]
#else
[Fact(Skip = ".NET Framework 4.8 doesn't optimize these methods")]
#endif
public void DoesNotEnumerateListsWhenCalledWithoutPredicate()
{
var list = new FailOnEnumerateListWrapper<string>(["foo"]);
_ = list.FirstOrNone();
_ = list.LastOrNone();

// SingleOrNone does not specialize for `Select`ed lists.
Assert.Throws<XunitException>(() => _ = list.SingleOrNone());
}

public static TheoryData<List<int>, List<string>> ValueReferenceEnumerables()
=> new()
{
Expand Down
25 changes: 25 additions & 0 deletions Funcky.Test/TestUtils/FailOnEnumerateCollectionWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Collections;
using Xunit.Sdk;

namespace Funcky.Test.TestUtils;

internal class FailOnEnumerateCollectionWrapper<T>(ICollection<T> collection) : ICollection<T>
{
public bool IsReadOnly => true;

public int Count => collection.Count;

public IEnumerator<T> GetEnumerator() => throw new XunitException("Should not be enumerated");

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

public void Add(T item) => throw new NotSupportedException();

public void Clear() => throw new NotSupportedException();

public bool Contains(T item) => collection.Contains(item);

public void CopyTo(T[] array, int arrayIndex) => collection.CopyTo(array, arrayIndex);

public bool Remove(T item) => throw new NotSupportedException();
}
16 changes: 16 additions & 0 deletions Funcky.Test/TestUtils/FailOnEnumerateListWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Funcky.Test.TestUtils;

internal sealed class FailOnEnumerateListWrapper<T>(IList<T> list) : FailOnEnumerateCollectionWrapper<T>(list), IList<T>
{
public T this[int index]
{
get => list[index];
set => throw new NotSupportedException();
}

public int IndexOf(T item) => list.IndexOf(item);

public void Insert(int index, T item) => throw new NotSupportedException();

public void RemoveAt(int index) => throw new NotSupportedException();
}
4 changes: 3 additions & 1 deletion Funcky/Extensions/EnumerableExtensions/FirstOrNone.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ public static partial class EnumerableExtensions
[Pure]
public static Option<TSource> FirstOrNone<TSource>(this IEnumerable<TSource> source)
where TSource : notnull
=> source.FirstOrNone(True);
=> source
.Select(Option.Some)
.FirstOrDefault();

/// <summary>
/// Returns the first element of the sequence as an <see cref="Option{T}" /> that satisfies a condition or a <see cref="Option{T}.None" /> value if no such element is found.
Expand Down
4 changes: 3 additions & 1 deletion Funcky/Extensions/EnumerableExtensions/LastOrNone.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ public static partial class EnumerableExtensions
[Pure]
public static Option<TSource> LastOrNone<TSource>(this IEnumerable<TSource> source)
where TSource : notnull
=> source.LastOrNone(True);
=> source
.Select(Option.Some)
.LastOrDefault();

/// <summary>
/// Returns the last element of a sequence that satisfies a condition as an <see cref="Option{T}" /> or a <see cref="Option{T}.None" /> value if no such element is found.
Expand Down
1 change: 0 additions & 1 deletion Funcky/Monads/Option/OptionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.ComponentModel;

#pragma warning disable RS0026
namespace Funcky.Monads;

public static partial class OptionExtensions
Expand Down