Skip to content

Commit

Permalink
Add WhenAny extension method
Browse files Browse the repository at this point in the history
  • Loading branch information
anaisbetts committed Feb 5, 2011
1 parent d62f4d8 commit 6bba919
Show file tree
Hide file tree
Showing 6 changed files with 836 additions and 0 deletions.
42 changes: 42 additions & 0 deletions ReactiveUI.Tests/ReactiveNotifyPropertyChangedMixinTest.cs
Expand Up @@ -206,5 +206,47 @@ public void MultiPropertyExpressionsShouldBeProperlyResolved()
data[x.input].AssertAreEqual(x.output);
}
}

[Fact]
public void WhenAnySmokeTest()
{
(new TestScheduler()).With(sched => {
var fixture = new HostTestFixture() {Child = new TestFixture()};
fixture.SomeOtherParam = 5;
fixture.Child.IsNotNullString = "Foo";
var output1 = new List<IObservedChange<HostTestFixture, int>>();
var output2 = new List<IObservedChange<HostTestFixture, string>>();
fixture.WhenAny(x => x.SomeOtherParam, x => x.Child.IsNotNullString, (sop, nns) => new {sop, nns}).Subscribe(x => {
output1.Add(x.sop); output2.Add(x.nns);
});
sched.Run();
Assert.Equal(1, output1.Count);
Assert.Equal(1, output2.Count);
Assert.Equal(fixture, output1[0].Sender);
Assert.Equal(fixture, output2[0].Sender);
Assert.Equal(5, output1[0].Value);
Assert.Equal("Foo", output2[0].Value);
fixture.SomeOtherParam = 10;
sched.Run();
Assert.Equal(2, output1.Count);
Assert.Equal(2, output2.Count);
Assert.Equal(fixture, output1[1].Sender);
Assert.Equal(fixture, output2[1].Sender);
Assert.Equal(10, output1[1].Value);
Assert.Equal("Foo", output2[1].Value);
fixture.Child.IsNotNullString = "Bar";
sched.Run();
Assert.Equal(3, output1.Count);
Assert.Equal(3, output2.Count);
Assert.Equal(fixture, output1[2].Sender);
Assert.Equal(fixture, output2[2].Sender);
Assert.Equal(10, output1[2].Value);
Assert.Equal("Bar", output2[2].Value);
});
}
}
}
11 changes: 11 additions & 0 deletions ReactiveUI/ObservedChangedMixin.cs
Expand Up @@ -64,6 +64,17 @@ public static class ObservedChangedMixin
return true;
}

internal static IObservedChange<TSender, TValue> fillInValue<TSender, TValue>(this IObservedChange<TSender, TValue> This)
{
// XXX: This is an internal method because I'm unsafely upcasting,
// but in certain cases it's needed.
var ret = (ObservedChange<TSender, TValue>)This;
var val = default(TValue);
This.TryGetValue(out val);
ret.Value = val;
return ret;
}

public static void SetValueToProperty<TSender, TValue, TTarget>(
this IObservedChange<TSender, TValue> This,
TTarget target,
Expand Down
34 changes: 34 additions & 0 deletions ReactiveUI/ReactiveNotifyPropertyChangedMixin.cs
Expand Up @@ -204,6 +204,40 @@ static string buildPropPathFromNodePtr(LinkedListNode<string> node)
Contract.Requires(selector != null);
return This.ObservableForProperty(property, beforeChange).Select(x => selector(x.Value));
}

/* NOTE: This is left here for reference - the real one is expanded out
* to 10 parameters in VariadicTemplates.tt */
#if FALSE
public static IObservable<TRet> WhenAny<TSender, T1, T2, TRet>(this TSender This,
Expression<Func<TSender, T1>> property1,
Expression<Func<TSender, T2>> property2,
Func<IObservedChange<TSender, T1>, IObservedChange<TSender, T2>, TRet> selector)
where TSender : IReactiveNotifyPropertyChanged
{
var slot1 = new ObservedChange<TSender, T1>() {
Sender = This,
PropertyName = String.Join(".", RxApp.expressionToPropertyNames(property1)),
};
T1 slot1Value = default(T1); slot1.TryGetValue(out slot1Value); slot1.Value = slot1Value;

var slot2 = new ObservedChange<TSender, T2>() {
Sender = This,
PropertyName = String.Join(".", RxApp.expressionToPropertyNames(property2)),
};
T2 slot2Value = default(T2); slot2.TryGetValue(out slot2Value); slot2.Value = slot2Value;

IObservedChange<TSender, T1> islot1 = slot1;
IObservedChange<TSender, T2> islot2 = slot2;
return Observable.CreateWithDisposable<TRet>(subject => {
subject.OnNext(selector(slot1, slot2));
return Observable.Merge(
This.ObservableForProperty(property1).Do(x => { lock (slot1) { islot1 = x.fillInValue(); } }).Select(x => selector(islot1, islot2)),
This.ObservableForProperty(property2).Do(x => { lock (slot2) { islot2 = x.fillInValue(); } }).Select(x => selector(islot1, islot2))
).Subscribe(subject);
});
}
#endif
}
}

Expand Down
14 changes: 14 additions & 0 deletions ReactiveUI/ReactiveUI.csproj
Expand Up @@ -129,6 +129,20 @@
<Compile Include="Schedulers.cs" />
<Compile Include="TestUtils.cs" />
<Compile Include="Validation.cs" />
<Compile Include="VariadicTemplates.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>VariadicTemplates.tt</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="VariadicTemplates.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>VariadicTemplates.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Expand Down

0 comments on commit 6bba919

Please sign in to comment.