Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
15 changes: 15 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,21 @@ src/
│ └── Domain/ # Test domain types using Bogus fakers
```

## Usings and Namespaces

**Do not add `using` directives to source files.** Every project has `ImplicitUsings` enabled and declares its namespaces as `<Using>` items: shared ones in `src/Directory.Build.props`, the rest in each csproj. If you need one that is not in scope, add it there rather than to the file.

Four opt-in namespaces are the exception and **must** stay file-level:

| Namespace | Why |
|---|---|
| `DynamicData.Alias` | Supplies `Select`, `Where` and `SelectMany` for changesets, so it changes what LINQ-looking code means. |
| `DynamicData.PLinq` | Supplies `Filter`, `Transform` and `SubscribeMany` under the *same names* as the core operators, but parallel. |
| `DynamicData.Aggregation` | Supplies `Count`, `Sum`, `Avg` and `Maximum`, which overlap Rx and LINQ names. |
| `DynamicData.Experimental` | Unstable API. Opting in should be visible at the call site. |

If globalising a namespace makes a name ambiguous, qualify the call site or add a global alias. Do not move the namespace back into the file. Already handled: `System.Reactive.Notification<T>` against `DynamicData.Internal.Notification<T>`, `Enumerable.ToHashSet` against `Kernel.EnumerableEx`, and `Person`, which the tests project aliases to the domain type so it wins over `Bogus.Person`.

## Operator Architecture Pattern

Most operators follow the same two-part pattern:
Expand Down
14 changes: 14 additions & 0 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<IsTestProject>$(MSBuildProjectName.Contains('Tests'))</IsTestProject>
<IsBenchmarkProject>$(MSBuildProjectName.Contains('Benchmarks'))</IsBenchmarkProject>
<DebugType>embedded</DebugType>
<ImplicitUsings>enable</ImplicitUsings>
<Authors>Roland Pheasant</Authors>
<Owners>Roland Pheasant</Owners>
<Copyright>Copyright (c) Roland Pheasant 2011-$([System.DateTime]::Now.ToString(yyyy))</Copyright>
Expand All @@ -36,6 +37,19 @@
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

<!-- Namespaces used by every project. Project specific ones live in the csproj that needs them. -->

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll stand by what I said in the Primitives PR: I only mildly dislike the idea of global usings in general, but I VERY much dislike the idea of moving code into config files. I would much rather see these as global using statements in .cs files, for each project.

<ItemGroup>
<Using Include="DynamicData.Binding" />
<Using Include="DynamicData.Kernel" />
<Using Include="System.Collections.ObjectModel" />
<Using Include="System.ComponentModel" />
<Using Include="System.Reactive.Disposables" />
<Using Include="System.Reactive.Linq" />
<Using Include="System.Reactive.Subjects" />
<Using Include="System.Reflection" />
<Using Include="System.Runtime.CompilerServices" />
</ItemGroup>

<PropertyGroup Condition="$(IsTestProject) OR $(IsBenchmarkProject)">
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet></CodeAnalysisRuleSet>
Expand Down
12 changes: 1 addition & 11 deletions src/DynamicData.Benchmarks/Cache/DeliveryQueueBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
// Copyright (c) 2011-2025 Roland Pheasant. All rights reserved.
// Copyright (c) 2011-2025 Roland Pheasant. All rights reserved.
// Roland Pheasant licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;

using BenchmarkDotNet.Attributes;

using DynamicData.Binding;

namespace DynamicData.Benchmarks.Cache;

/// <summary>
Expand Down
4 changes: 0 additions & 4 deletions src/DynamicData.Benchmarks/Cache/DisposeMany_Cache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
// Roland Pheasant licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;

using BenchmarkDotNet.Attributes;

namespace DynamicData.Benchmarks.Cache
{
[MemoryDiagnoser]
Expand Down
11 changes: 1 addition & 10 deletions src/DynamicData.Benchmarks/Cache/EditDiff.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;

using BenchmarkDotNet.Attributes;

using DynamicData.Kernel;

namespace DynamicData.Benchmarks.Cache;
namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
Expand Down
12 changes: 2 additions & 10 deletions src/DynamicData.Benchmarks/Cache/ExpireAfter_Cache_ForSource.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
using System;
using System.Collections.Immutable;
using System.Linq;

using BenchmarkDotNet.Attributes;

using Bogus;

namespace DynamicData.Benchmarks.Cache;
namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
Expand Down Expand Up @@ -79,7 +71,7 @@ public void RandomizedEditsAndExpirations()
private void PerformRandomizedEdits(SourceCache<Item, int> source)
{
var randomizer = new Randomizer(1234567);

var nextItemIndex = 0;

for (var i = 0; i < _editCount; ++i)
Expand Down
11 changes: 1 addition & 10 deletions src/DynamicData.Benchmarks/Cache/ExpireAfter_Cache_ForStream.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Reactive.Subjects;

using BenchmarkDotNet.Attributes;

using Bogus;

namespace DynamicData.Benchmarks.Cache;
namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
Expand Down
8 changes: 1 addition & 7 deletions src/DynamicData.Benchmarks/Cache/FilterImmutable.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Reactive.Subjects;

using BenchmarkDotNet.Attributes;

namespace DynamicData.Benchmarks.Cache;
namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Reactive.Subjects;

using BenchmarkDotNet.Attributes;

using Bogus;

namespace DynamicData.Benchmarks.Cache;
namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
Expand Down Expand Up @@ -99,7 +90,6 @@ public Filter_Cache_WithPredicateState()
}
_changeSets = changeSets.MoveToImmutable();


var predicateStates = ImmutableArray.CreateBuilder<int>(initialCapacity: 5_000);
while (predicateStates.Count < predicateStates.Capacity)
predicateStates.Add(randomizer.Int());
Expand Down Expand Up @@ -150,7 +140,7 @@ public static bool FilterByIdInclusionMask(
int idInclusionMask,
Item item)
=> ((item.Id & idInclusionMask) == 0) && item.IsIncluded;

public required int Id { get; init; }

public bool IsIncluded { get; set; }
Expand Down
26 changes: 4 additions & 22 deletions src/DynamicData.Benchmarks/Cache/SortAndBindChange.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Subjects;
using BenchmarkDotNet.Attributes;
using DynamicData.Binding;

namespace DynamicData.Benchmarks.Cache;
namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
Expand All @@ -19,7 +11,6 @@ private record Item(string Name, int Id, int Ranking);
.Ascending(i => i.Ranking)
.ThenByAscending(i => i.Name);


Subject<IChangeSet<Item, int>> _newSubject = new();
Subject<IChangeSet<Item, int>> _newSubjectOptimised = new();
Subject<IChangeSet<Item, int>> _oldSubject = new();
Expand All @@ -32,12 +23,9 @@ private record Item(string Name, int Id, int Ranking);
private ReadOnlyObservableCollection<Item>? _oldList;
private ReadOnlyObservableCollection<Item>? _oldListOptimised;



[Params(10, 100, 1_000, 10_000, 50_000)]
public int Count { get; set; }


[GlobalSetup]
public void SetUp()
{
Expand All @@ -46,16 +34,15 @@ public void SetUp()
_newSubject = new Subject<IChangeSet<Item, int>>();
_newSubjectOptimised = new Subject<IChangeSet<Item, int>>();


_cleanUp = new CompositeDisposable
_cleanUp = new CompositeDisposable
(
_newSubject.SortAndBind(out var newList, _comparer).Subscribe(),
_newSubjectOptimised.SortAndBind(out var optimisedList, _comparer, new SortAndBindOptions
{
InitialCapacity = Count,
UseBinarySearch = true
}).Subscribe(),

_oldSubject.Sort(_comparer).Bind(out var oldList).Subscribe(),
_oldSubjectOptimised.Sort(_comparer, SortOptimisations.ComparesImmutableValuesOnly).Bind(out var oldOptimisedList).Subscribe()
);
Expand All @@ -65,8 +52,6 @@ public void SetUp()
_oldList = oldList;
_oldListOptimised = oldOptimisedList;



var changeSet = new ChangeSet<Item, int>(Count);
foreach (var i in Enumerable.Range(1, Count))
{
Expand All @@ -84,7 +69,6 @@ public void SetUp()
[Benchmark(Baseline = true)]
public void Old() => RunTest(_oldSubject, _oldList!);


[Benchmark]
public void OldOptimized() => RunTest(_oldSubjectOptimised, _oldListOptimised!);

Expand All @@ -94,7 +78,6 @@ public void SetUp()
[Benchmark]
public void NewOptimized() => RunTest(_newSubjectOptimised, _newListOptimised!);


void RunTest(Subject<IChangeSet<Item, int>> subject, ReadOnlyObservableCollection<Item> list)
{
var original = list[Count / 2];
Expand All @@ -106,6 +89,5 @@ void RunTest(Subject<IChangeSet<Item, int>> subject, ReadOnlyObservableCollectio
});
}


public void Dispose() => _cleanUp?.Dispose();
}
}
16 changes: 2 additions & 14 deletions src/DynamicData.Benchmarks/Cache/SortAndBindInitial.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
using BenchmarkDotNet.Attributes;
using System;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Subjects;
using DynamicData.Binding;

namespace DynamicData.Benchmarks.Cache;
namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
Expand All @@ -17,7 +10,6 @@ private record Item(string Name, int Id, int Ranking);

private readonly SortExpressionComparer<Item> _comparer = SortExpressionComparer<Item>.Ascending(i => i.Ranking).ThenByAscending(i => i.Name);


Subject<IChangeSet<Item, int>> _newSubject = new();
Subject<IChangeSet<Item, int>> _newSubjectOptimised = new();
Subject<IChangeSet<Item, int>> _oldSubject = new();
Expand All @@ -26,11 +18,9 @@ private record Item(string Name, int Id, int Ranking);
private IDisposable? _cleanUp;
private ChangeSet<Item, int>? _changeSet;


[Params(10, 100, 1_000, 10_000, 50_000)]
public int Count { get; set; }


[GlobalSetup]
public void SetUp()
{
Expand Down Expand Up @@ -63,7 +53,6 @@ public void SetUp()
);
}


[Benchmark(Baseline = true)]
public void Old() => _oldSubject.OnNext(_changeSet!);

Expand All @@ -76,6 +65,5 @@ public void SetUp()
[Benchmark]
public void NewOptimized() => _newSubjectOptimised.OnNext(_changeSet!);


public void Dispose() => _cleanUp?.Dispose();
}
}
5 changes: 0 additions & 5 deletions src/DynamicData.Benchmarks/Cache/SourceCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@
// Roland Pheasant licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System.Linq;

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;

namespace DynamicData.Benchmarks.Cache
{
public class BenchmarkItem
Expand Down
8 changes: 1 addition & 7 deletions src/DynamicData.Benchmarks/Cache/StatelessFiltering.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Reactive.Subjects;

using BenchmarkDotNet.Attributes;

namespace DynamicData.Benchmarks.Cache;
namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
Expand Down
9 changes: 1 addition & 8 deletions src/DynamicData.Benchmarks/Cache/StatelessTransforming.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Subjects;

using BenchmarkDotNet.Attributes;

namespace DynamicData.Benchmarks.Cache;
namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
Expand Down
10 changes: 1 addition & 9 deletions src/DynamicData.Benchmarks/Cache/ToObservableChangeSet_Cache.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Subjects;

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;

namespace DynamicData.Benchmarks.Cache;
namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
Expand Down
9 changes: 1 addition & 8 deletions src/DynamicData.Benchmarks/Cache/TransformImmutable.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Subjects;

using BenchmarkDotNet.Attributes;

namespace DynamicData.Benchmarks.Cache;
namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
Expand Down
9 changes: 1 addition & 8 deletions src/DynamicData.Benchmarks/Cache/TransformMany.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;

using BenchmarkDotNet.Attributes;

namespace DynamicData.Benchmarks.Cache;
namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
Expand Down
Loading
Loading