├── .editorconfig
├── .github
├── renovate.json
└── workflows
│ ├── ci-build.yml
│ ├── lock.yml
│ └── release.yml
├── .gitignore
├── LICENSE
├── NuGet.Config
├── README.md
├── docs
├── Performance.md
├── ReactiveMarbles.PropertyChanged.Benchmarks.BindBenchmarks-report-github.md
└── ReactiveMarbles.PropertyChanged.Benchmarks.PropertyChangesBenchmarks-report-github.md
├── images
└── logo.png
├── nuget.config
├── src
├── Directory.Build.props
├── Directory.build.targets
├── ReactiveMarbles.PropertyChanged.Benchmarks
│ ├── .editorconfig
│ ├── BindBenchmarks.cs
│ ├── BindBenchmarks.tt
│ ├── Directory.Build.props
│ ├── Legacy
│ │ ├── BindExtensions.cs
│ │ ├── ExpressionExtensions.cs
│ │ ├── GetMemberFuncCache.cs
│ │ ├── NotifyPropertyChangedExtensions.cs
│ │ └── SetMemberFuncCache.cs
│ ├── Moqs
│ │ └── TestClass.cs
│ ├── Program.cs
│ ├── ReactiveMarbles.PropertyChanged.Benchmarks.csproj
│ ├── WhenChangedBenchmarks.cs
│ └── WhenChangedBenchmarks.tt
├── ReactiveMarbles.PropertyChanged.SourceGenerator.Benchmarks
│ ├── .editorconfig
│ ├── Directory.Build.props
│ ├── Program.cs
│ ├── ReactiveMarbles.PropertyChanged.SourceGenerator.Benchmarks.csproj
│ ├── WhenChangedBenchmarks.cs
│ └── WhenChangedBenchmarks.tt
├── ReactiveMarbles.PropertyChanged.SourceGenerator.Builders
│ ├── AccessibilityExtensions.cs
│ ├── BaseUserSourceBuilder.cs
│ ├── BindHostBuilder.cs
│ ├── BindHostProxy.cs
│ ├── CompilationUtil.cs
│ ├── EmptyClassBuilder.cs
│ ├── HostProxyBase.cs
│ ├── InvocationKind.cs
│ ├── MethodNames.cs
│ ├── ReactiveMarbles.PropertyChanged.SourceGenerator.Builders.csproj
│ ├── ReceiverKind.cs
│ ├── ReflectionUtil.cs
│ ├── WhenChangedHostBuilder.cs
│ └── WhenChangedHostProxy.cs
├── ReactiveMarbles.PropertyChanged.SourceGenerator.Sample
│ ├── OtherNamespace
│ │ └── SampleClass.cs
│ ├── Program.cs
│ ├── ReactiveMarbles.PropertyChanged.SourceGenerator.Sample.csproj
│ ├── SampleClass.cs
│ └── SampleClass2.cs
├── ReactiveMarbles.PropertyChanged.SourceGenerator.Tests
│ ├── AccessibilityTestCases.cs
│ ├── AccessibilityTestCases.tt
│ ├── BindFixture.cs
│ ├── BindGeneratorTestsDiagnostics.cs
│ ├── BindGeneratorTestsNoDiagnostics.cs
│ ├── CommonTest.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── ReactiveMarbles.PropertyChanged.SourceGenerator.Tests.csproj
│ ├── SampleTests.cs
│ ├── TestHelper.cs
│ ├── WhenChangedFixture.cs
│ ├── WhenChangedGeneratorTestsDiagnostics.cs
│ ├── WhenChangedGeneratorTestsNoDiagnostics.cs
│ ├── WhenChangingGeneratorTestsDiagnostics.cs
│ ├── WhenChangingGeneratorTestsNoDiagnostics.cs
│ └── xunit.runner.json
├── ReactiveMarbles.PropertyChanged.SourceGenerator
│ ├── AnalyzerReleases.Shipped.md
│ ├── AnalyzerReleases.Unshipped.md
│ ├── Comparers
│ │ └── SyntaxNodeComparer.cs
│ ├── Constants.Binding.cs
│ ├── Constants.When.cs
│ ├── Constants.When.tt
│ ├── Constants.cs
│ ├── DiagnosticWarnings.cs
│ ├── Generator.cs
│ ├── Helpers
│ │ ├── AccessibilityExtensions.cs
│ │ ├── Extensions.cs
│ │ ├── GeneratorHelpers.cs
│ │ ├── RoslynExtensions.cs
│ │ └── SourceHelpers.cs
│ ├── IsExternalInit.cs
│ ├── MethodCreators
│ │ ├── MethodCreator.BindCommon.cs
│ │ ├── MethodCreator.BindOneWay.cs
│ │ ├── MethodCreator.BindTwoWay.cs
│ │ ├── MethodCreator.WhenCommon.cs
│ │ ├── MethodCreator.cs
│ │ └── Transient
│ │ │ ├── BindStatementsDatum.cs
│ │ │ ├── ClassDatum.cs
│ │ │ ├── CompilationDatum.cs
│ │ │ ├── ExpressionArgument.cs
│ │ │ ├── ExpressionChain.cs
│ │ │ ├── MethodDatum.cs
│ │ │ ├── MultiWhenStatementsDatum.cs
│ │ │ ├── NamespaceDatum.cs
│ │ │ └── WhenStatementsDatum.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── ReactiveMarbles - Backup.PropertyChanged.SourceGenerator.csproj
│ ├── ReactiveMarbles.PropertyChanged.SourceGenerator.csproj
│ ├── ReactiveMarbles.PropertyChanged.SourceGenerator.csproj.DotSettings
│ ├── SyntaxReceiver.cs
│ └── TypeSymbolComparer.cs
├── ReactiveMarbles.PropertyChanged.Tests
│ ├── BindTests.cs
│ ├── Moqs
│ │ ├── A.cs
│ │ ├── B.cs
│ │ ├── BaseTestClass.cs
│ │ └── C.cs
│ ├── ReactiveMarbles.PropertyChanged.Tests.csproj
│ └── WhenChangedTests.cs
├── ReactiveMarbles.PropertyChanged.sln
├── ReactiveMarbles.PropertyChanged
│ ├── BindExtensions.cs
│ ├── ExpressionExtensions.cs
│ ├── GetMemberFuncCache.cs
│ ├── MemberFuncCacheKeyComparer.cs
│ ├── NotifyPropertiesChangeExtensions.cs
│ ├── NotifyPropertiesChangeExtensions.tt
│ ├── NotifyPropertyChangedExtensions.cs
│ └── ReactiveMarbles.PropertyChanged.csproj
├── global.json
└── stylecop.json
└── version.json
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": ["local>reactivemarbles/.github:renovate"]
4 | }
--------------------------------------------------------------------------------
/.github/workflows/ci-build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | env:
10 | configuration: Release
11 | productNamespacePrefix: "ReactiveMarbles"
12 |
13 | jobs:
14 | build:
15 | runs-on: windows-2022
16 | outputs:
17 | nbgv: ${{ steps.nbgv.outputs.SemVer2 }}
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v4.2.2
21 | with:
22 | fetch-depth: 0
23 | lfs: true
24 |
25 | - name: Install .NET 6 and 7
26 | uses: actions/setup-dotnet@v4.3.1
27 | with:
28 | dotnet-version: |
29 | 6.0.x
30 | 7.0.x
31 |
32 | - uses: nuget/setup-nuget@v2
33 | name: Setup NuGet
34 |
35 | - name: Install DotNet workloads
36 | shell: bash
37 | run: |
38 | dotnet workload install android
39 | dotnet workload install ios
40 | dotnet workload install tvos
41 | dotnet workload install macos
42 | dotnet workload install maui
43 | dotnet workload install maccatalyst
44 |
45 | - name: Add MSBuild to PATH
46 | uses: glennawatson/setup-msbuild@v1.0.3
47 | with:
48 | prerelease: true
49 |
50 | - name: NBGV
51 | id: nbgv
52 | uses: dotnet/nbgv@master
53 | with:
54 | setAllVars: true
55 |
56 | - name: NuGet Restore
57 | run: nuget restore
58 | working-directory: src
59 |
60 | - name: Build
61 | run: msbuild /t:build,pack /nowarn:MSB4011 /maxcpucount /p:NoPackageAnalysis=true /verbosity:minimal /p:Configuration=${{ env.configuration }}
62 | working-directory: src
63 |
64 |
65 | - name: Run Unit Tests and Generate Coverage
66 | uses: glennawatson/coverlet-msbuild@v2
67 | with:
68 | project-files: '**/*Tests*.csproj'
69 | no-build: true
70 | exclude-filter: '[${{env.productNamespacePrefix}}.*.Tests.*]*'
71 | include-filter: '[${{env.productNamespacePrefix}}*]*'
72 | output-format: cobertura
73 | output: '../../artifacts/'
74 | configuration: ${{ env.configuration }}
75 |
76 | - name: Upload Code Coverage
77 | shell: bash
78 | run: |
79 | echo $PWD
80 | bash <(curl -s https://codecov.io/bash) -X gcov -X coveragepy -t ${{ env.CODECOV_TOKEN }} -s '$PWD/artifacts' -f '*.xml'
81 | env:
82 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
83 |
84 | - name: Create NuGet Artifacts
85 | uses: actions/upload-artifact@master
86 | with:
87 | name: nuget
88 | path: '**/*.nupkg'
89 |
--------------------------------------------------------------------------------
/.github/workflows/lock.yml:
--------------------------------------------------------------------------------
1 | name: 'Lock Threads'
2 |
3 | on:
4 | schedule:
5 | - cron: '0 0 * * *'
6 | workflow_dispatch:
7 |
8 | permissions:
9 | issues: write
10 | pull-requests: write
11 |
12 | concurrency:
13 | group: lock
14 |
15 | jobs:
16 | action:
17 | runs-on: ubuntu-latest
18 | steps:
19 | - uses: dessant/lock-threads@v5
20 | with:
21 | github-token: ${{ github.token }}
22 | issue-inactive-days: '14'
23 | pr-inactive-days: '14'
24 | issue-comment: >
25 | This issue has been automatically locked since there
26 | has not been any recent activity after it was closed.
27 | Please open a new issue for related bugs.
28 | pr-comment: >
29 | This pull request has been automatically locked since there
30 | has not been any recent activity after it was closed.
31 | Please open a new issue for related bugs.
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Build and Release
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 |
7 | env:
8 | configuration: Release
9 | productNamespacePrefix: "ReactiveMarbles"
10 |
11 | jobs:
12 | build:
13 | runs-on: windows-2022
14 | environment:
15 | name: release
16 | outputs:
17 | nbgv: ${{ steps.nbgv.outputs.SemVer2 }}
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v4.2.2
21 | with:
22 | fetch-depth: 0
23 | lfs: true
24 |
25 | - name: Install .NET 6
26 | uses: actions/setup-dotnet@v4.3.1
27 | with:
28 | dotnet-version: 6.0.x
29 | include-prerelease: true
30 |
31 | - uses: nuget/setup-nuget@v2
32 | name: Setup NuGet
33 |
34 | - name: Install DotNet workloads
35 | shell: bash
36 | run: |
37 | dotnet workload install android
38 | dotnet workload install ios
39 | dotnet workload install tvos
40 | dotnet workload install macos
41 | dotnet workload install maui
42 | dotnet workload install maccatalyst
43 |
44 | - name: Add MSBuild to PATH
45 | uses: glennawatson/setup-msbuild@v1.0.3
46 | with:
47 | prerelease: true
48 |
49 | - name: NBGV
50 | id: nbgv
51 | uses: dotnet/nbgv@master
52 | with:
53 | setAllVars: true
54 |
55 | - name: NuGet Restore
56 | run: nuget restore
57 | working-directory: src
58 |
59 | - name: Build
60 | run: msbuild /t:build,pack /nowarn:MSB4011 /maxcpucount /p:NoPackageAnalysis=true /verbosity:minimal /p:Configuration=${{ env.configuration }}
61 | working-directory: src
62 |
63 | # Decode the base 64 encoded pfx and save the Signing_Certificate
64 | - name: Sign NuGet packages
65 | shell: pwsh
66 | run: |
67 | $pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.SIGNING_CERTIFICATE }}")
68 | [IO.File]::WriteAllBytes("GitHubActionsWorkflow.pfx", $pfx_cert_byte)
69 | $secure_password = ConvertTo-SecureString ${{ secrets.SIGN_CERTIFICATE_PASSWORD }} –asplaintext –force
70 | Import-PfxCertificate -FilePath GitHubActionsWorkflow.pfx -Password $secure_password -CertStoreLocation Cert:\CurrentUser\My
71 | nuget sign -Timestamper http://timestamp.digicert.com -CertificateFingerprint ${{ secrets.SIGN_CERTIFICATE_HASH }} **/*.nupkg
72 |
73 | - name: Changelog
74 | uses: glennawatson/ChangeLog@v1.1
75 | id: changelog
76 |
77 | - name: Create Release
78 | uses: actions/create-release@v1.1.4
79 | env:
80 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
81 | with:
82 | tag_name: ${{ steps.nbgv.outputs.SemVer2 }}
83 | release_name: ${{ steps.nbgv.outputs.SemVer2 }}
84 | body: |
85 | ${{ steps.changelog.outputs.commitLog }}
86 |
87 | - name: NuGet Push
88 | env:
89 | NUGET_AUTH_TOKEN: ${{ secrets.NUGET_API_KEY }}
90 | SOURCE_URL: https://api.nuget.org/v3/index.json
91 | run: |
92 | dotnet nuget push -s ${{ env.SOURCE_URL }} -k ${{ env.NUGET_AUTH_TOKEN }} **/*.nupkg
93 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-2021 ReactiveUI Association Incorporated
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Reactive Marbles Property Changed
2 |
3 | ## Packages
4 |
5 | | Container | NuGet
6 | |---------|-------|
7 | | [PropertyChanged][PropertyChanged] | [![PropertyChangedBadge]][PropertyChanged]
8 | | [PropertyChanged.SourceGenerator][PropertyChangedSourceGen] | [![PropertyChangedSourceGenBadge]][PropertyChangedSourceGen]
9 |
10 | [PropertyChanged]: https://www.nuget.org/packages/ReactiveMarbles.PropertyChanged/
11 | [PropertyChangedBadge]: https://img.shields.io/nuget/v/ReactiveMarbles.PropertyChanged.svg
12 | [PropertyChangedSourceGen]: https://www.nuget.org/packages/ReactiveMarbles.PropertyChanged.SourceGenerator/
13 | [PropertyChangedSourceGenBadge]: https://img.shields.io/nuget/v/ReactiveMarbles.PropertyChanged.SourceGenerator.svg
14 |
15 | ## Overview
16 |
17 | A framework for providing an observable with the latest value of a property expression.
18 |
19 | The source generator version will generate raw source code for the binding. If you have private/protected classes and/or properties it may require partial classes.
20 |
21 | The regular version will use Expression trees on platforms that support it (no iOS based platforms). On iOS it will just use reflection. This provides a roughly 2x performance boost for those platforms that can use expression trees.
22 |
23 | ```cs
24 | this.WhenChanged(x => x.Property1.Property2.Property3);
25 | ```
26 |
27 | The above will generate a `IObservable` where T is the type of `Property3`. It will signal each time a value has changed. It is aware of all property changes in the property chain.
28 |
29 | ## Binding
30 |
31 | There are several methods of binding.
32 |
33 | First is two way binding. Two way binding will update either the `host` or the `target` whenever the target property has changed.
34 |
35 | ```cs
36 | host.BindTwoWay(target, host => host.B.C, target => target.D.E);
37 | ```
38 |
39 | One way binding will only update the `target` with changes the `host`'s specified target property.
40 |
41 | ```cs
42 | host.BindOneWay(target, host => host.B.C);
43 | ```
44 |
45 | There are also overloads with lambdas that allow you to convert from the `host` to the `target`. These will allow you to convert at binding time to the specified formats.
46 |
47 | ```cs
48 | host.BindOneWay(target, host => host.B.C, hostProp => ConvertToTargetPropType(hostProp));
49 | host.BindTwoWay(target, host => host.B.C, target => target.D.E, hostProp => ConvertToTargetPropType(hostProp), targetProp => ConvertToHostPropType(targetProp));
50 | ```
51 |
52 | # Limitations compared to ReactiveUI
53 |
54 | At the moment it only supports `INotifyPropertyChanged` properties. More property types to come such as WPF DependencyProperty.
55 |
56 | # Milestones
57 |
58 | * Implement initial binding and property changes.
59 |
60 | # Benchmark Comparisons
61 |
62 | Detailed benchmarking results can be found [here](/docs/Performance.md).
63 |
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reactivemarbles/PropertyChanged/f9c06ab9694384fd1fb37f3b13902434bff2b2bd/images/logo.png
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 | AnyCPU
5 | $(MSBuildProjectName.Contains('Tests'))
6 | Glenn Watson
7 | Copyright (c) 2021 ReactiveUI Association Incorporated
8 | logo.png
9 | MIT
10 | https://github.com/reactivemarbles/PropertyChanged
11 | Allows to get an observables for property changed events.
12 | glennawatson
13 | system.reactive;propertychanged;inpc;reactive;functional
14 | https://github.com/reactivemarbles/PropertyChanged/releases
15 | https://github.com/reactivemarbles/PropertyChanged
16 | git
17 | true
18 | true
19 |
20 |
21 | true
22 |
23 | true
24 |
25 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
26 | True
27 | latest
28 |
29 |
30 |
31 | true
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | $(MSBuildThisFileDirectory)
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/src/Directory.build.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 | $(AssemblyName) ($(TargetFramework))
4 |
5 |
6 |
7 | $(DefineConstants);NET_45;XAML
8 |
9 |
10 | $(DefineConstants);NETFX_CORE;XAML;WINDOWS_UWP
11 |
12 |
13 | $(DefineConstants);MONO;UIKIT;COCOA
14 |
15 |
16 | $(DefineConstants);MONO;COCOA
17 |
18 |
19 | $(DefineConstants);MONO;UIKIT;COCOA
20 |
21 |
22 | $(DefineConstants);MONO;UIKIT;COCOA
23 |
24 |
25 | $(DefineConstants);MONO;ANDROID
26 |
27 |
28 | $(DefineConstants);TIZEN
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/ReactiveMarbles.PropertyChanged.Benchmarks/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | # override for benchmarks.
3 |
4 | # top-most EditorConfig file
5 | root = true
--------------------------------------------------------------------------------
/src/ReactiveMarbles.PropertyChanged.Benchmarks/BindBenchmarks.tt:
--------------------------------------------------------------------------------
1 | <#@ template debug="false" hostspecific="false" language="C#" #>
2 | <#@ assembly name="System.Core.dll" #>
3 | <#@ assembly name="System.Collections.dll" #>
4 | <#@ import namespace="System.Linq" #>
5 | <#@ output extension=".cs" #>
6 | // Copyright (c) 2019-2020 ReactiveUI Association Incorporated. All rights reserved.
7 | // ReactiveUI Association Incorporated licenses this file to you under the MIT license.
8 | // See the LICENSE file in the project root for full license information.
9 |
10 | using System;
11 | using BenchmarkDotNet.Attributes;
12 | using BenchmarkDotNet.Configs;
13 | using BenchmarkDotNet.Jobs;
14 |
15 | using ReactiveMarbles.PropertyChanged.Benchmarks.Moqs;
16 | <#
17 | // First entry will be classified as the baseline.
18 | var participants = new (string Alias, string MethodName, string FullClassName)[]
19 | {
20 | ("UI", "Bind", "ReactiveUI.PropertyBindingMixins"),
21 | ("Old", "Bind", "ReactiveMarbles.PropertyChanged.Benchmarks.Legacy.BindExtensions"),
22 | ("New", "Bind", "ReactiveMarbles.PropertyChanged.BindExtensions"),
23 | };
24 | var depths = new[] { 1, 2, 3 };
25 | const string BindAndChange = "BindAndChange";
26 | const string Change = "Change";
27 | string GetBenchmarkName(string baseName, int depth, string alias) => $"{baseName}_Depth{depth}_{alias}";
28 |
29 | foreach(var (Alias, MethodName, FullClassName) in participants)
30 | {
31 | #>
32 | using <#= Alias #> = <#= FullClassName #>;
33 | <#}#>
34 |
35 | namespace ReactiveMarbles.PropertyChanged.Benchmarks
36 | {
37 | ///
38 | /// Benchmarks for binding.
39 | ///
40 | [SimpleJob(RuntimeMoniker.NetCoreApp31)]
41 | [MemoryDiagnoser]
42 | [MarkdownExporterAttribute.GitHub]
43 | [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
44 | public class BindBenchmarks
45 | {
46 | private TestClass _from;
47 | private TestClass _to;
48 | private IDisposable _binding;
49 |
50 | ///
51 | /// The number mutations to perform.
52 | ///
53 | [Params(1, 10, 100, 1000)]
54 | public int Changes;
55 |
56 | <#
57 | for(var i = 0; i < depths.Length; i++)
58 | {
59 | var depth = depths[i]; #>
60 | [GlobalSetup(Targets = new[] { <#= string.Join(", ", participants.Select(x => $"\"{GetBenchmarkName(BindAndChange, depth, x.Alias)}\"")) #> })]
61 | public void Depth<#= depth #>Setup()
62 | {
63 | _from = new TestClass(<#= depth #>);
64 | _to = new TestClass(<#= depth #>);
65 | }
66 |
67 | <# } #>
68 | public void PerformMutations(int depth)
69 | {
70 | // We loop through the changes, alternating mutations to the source and destination at every depth.
71 | var d2 = depth * 2;
72 | for (var i = 0; i < Changes; ++i)
73 | {
74 | var a = i % d2;
75 | var t = (a % 2) > 0 ? _to : _from;
76 | t.Mutate(a / 2);
77 | }
78 | }
79 |
80 | <#
81 | for(var i = 0; i < depths.Length; i++)
82 | {
83 | var depth = depths[i];
84 | for (var j = 0; j < participants.Length; j++)
85 | {
86 | var (Alias, MethodName, FullClassName) = participants[j]; #>
87 | [BenchmarkCategory("Bind and Change Depth <#= depth #>")]
88 | <# if (j == 0) WriteLine("[Benchmark(Baseline = true)]"); else WriteLine("[Benchmark]"); #>
89 | public void <#= GetBenchmarkName(BindAndChange, depth, Alias) #>()
90 | {
91 | <#
92 | var expression = string.Join(".", Enumerable.Range(1, depth - 1).Select(x => "Child").Prepend("x").Append("Value")); #>
93 | using var binding = <#= $"{Alias}.{MethodName}(_from, _to, x => {expression}, x => {expression});" #>
94 | PerformMutations(<#= depth #>);
95 | }
96 |
97 | <# } #>
98 | <# } #>
99 | <#
100 | for(var i = 0; i < depths.Length; i++)
101 | {
102 | var depth = depths[i];
103 | for (var j = 0; j < participants.Length; j++)
104 | {
105 | var (Alias, MethodName, FullClassName) = participants[j];
106 | var benchmarkName = GetBenchmarkName(Change, depth, Alias); #>
107 | [GlobalSetup(Target = <#= $"\"{benchmarkName}\"" #>)]
108 | public void <#= $"{benchmarkName}Setup" #>()
109 | {
110 | <#
111 | var expression = string.Join(".", Enumerable.Range(1, depth - 1).Select(x => "Child").Prepend("x").Append("Value")); #>
112 | Depth<#= depth #>Setup();
113 | _binding = <#= $"{Alias}.{MethodName}(_from, _to, x => {expression}, x => {expression});" #>
114 | }
115 |
116 | [BenchmarkCategory("Change Depth <#= depth #>")]
117 | <# if (j == 0) WriteLine("[Benchmark(Baseline = true)]"); else WriteLine("[Benchmark]"); #>
118 | public void <#= benchmarkName #>()
119 | {
120 | PerformMutations(<#= depth #>);
121 | }
122 |
123 | <# } #>
124 | <#
125 | } #>
126 | [GlobalCleanup(Targets = new[] { <#= string.Join(", ", depths.SelectMany(depth => participants.Select(x => $"\"{GetBenchmarkName(Change, depth, x.Alias)}\""))) #> })]
127 | public void GlobalCleanup()
128 | {
129 | _binding.Dispose();
130 | }
131 | }
132 | }
--------------------------------------------------------------------------------
/src/ReactiveMarbles.PropertyChanged.Benchmarks/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/ReactiveMarbles.PropertyChanged.Benchmarks/Legacy/ExpressionExtensions.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2019-2020 ReactiveUI Association Incorporated. All rights reserved.
2 | // ReactiveUI Association Incorporated licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for full license information.
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Linq.Expressions;
9 |
10 | namespace ReactiveMarbles.PropertyChanged.Benchmarks.Legacy
11 | {
12 | internal static class ExpressionExtensions
13 | {
14 | internal static object GetParentForExpression(this List> chain, object startItem)
15 | {
16 | object current = startItem;
17 | foreach (Func