diff --git a/Bonsai.Core.Tests/OverloadedCombinatorBuilderTests.cs b/Bonsai.Core.Tests/OverloadedCombinatorBuilderTests.cs index 438549f7b..093481366 100644 --- a/Bonsai.Core.Tests/OverloadedCombinatorBuilderTests.cs +++ b/Bonsai.Core.Tests/OverloadedCombinatorBuilderTests.cs @@ -98,6 +98,40 @@ public IObservable Process(IObservable> s } } + class HidingOverloadedCombinatorMock : OverloadedCombinatorMock + { + public new IObservable Process(IObservable source) + { + return source.Select(x => double.NaN); + } + } + + class HidingSpecializedGenericOverloadedCombinatorMock : SpecializedGenericOverloadedCombinatorMock + { + public new IObservable Process(IObservable> source) + { + return source.Select(x => default(TSource)); + } + } + + [Combinator] + class BaseVirtualCombinatorMock + { + public virtual IObservable Process(IObservable source) => source; + } + + class DerivedOverrideCombinatorMock : BaseVirtualCombinatorMock + { + public override IObservable Process(IObservable source) => Observable.Return(string.Empty); + } + + class DerivedOverrideOverloadedCombinatorMock : BaseVirtualCombinatorMock + { + public override IObservable Process(IObservable source) => source; + + public IObservable Process(IObservable _) => Observable.Return(default(object)); + } + [TestMethod] public void Build_DoubleOverloadedMethodCalledWithDouble_ReturnsDoubleValue() { @@ -187,5 +221,49 @@ public void Build_SpecializedGenericOverloadedMethod_ReturnsValue() var result = Last(resultProvider).Result; Assert.AreEqual(value, result); } + + [TestMethod] + public void Build_HidingDoubleOverloadedMethodCalledWithDouble_ReturnsDoubleValue() + { + var value = 5.0; + var combinator = new HidingOverloadedCombinatorMock(); + var source = CreateObservableExpression(Observable.Return(value)); + var resultProvider = TestCombinatorBuilder(combinator, source); + var result = Last(resultProvider).Result; + Assert.AreNotEqual(value, result); + } + + [TestMethod] + public void Build_HidingSpecializedGenericOverloadedMethod_ReturnsValue() + { + var value = 5; + var combinator = new HidingSpecializedGenericOverloadedCombinatorMock(); + var source = CreateObservableExpression(Observable.Return(value).Timestamp()); + var resultProvider = TestCombinatorBuilder(combinator, source); + var result = Last(resultProvider).Result; + Assert.AreNotEqual(value, result); + } + + [TestMethod] + public void Build_DerivedOverrideMethodCalledWithString_ReturnsOverrideValue() + { + var value = "5"; + var combinator = new DerivedOverrideCombinatorMock(); + var source = CreateObservableExpression(Observable.Return(value)); + var resultProvider = TestCombinatorBuilder(combinator, source); + var result = Last(resultProvider).Result; + Assert.AreNotEqual(value, result); + } + + [TestMethod] + public void Build_DerivedOverrideOverloadedMethodCalledWithString_ReturnsObjectValue() + { + var value = "5"; + var combinator = new DerivedOverrideOverloadedCombinatorMock(); + var source = CreateObservableExpression(Observable.Return(value)); + var resultProvider = TestCombinatorBuilder(combinator, source); + var result = Last(resultProvider).Result; + Assert.AreNotEqual(value, result); + } } } diff --git a/Bonsai.Core/Expressions/ExpressionBuilder.cs b/Bonsai.Core/Expressions/ExpressionBuilder.cs index f0196c4b7..126b9974a 100644 --- a/Bonsai.Core/Expressions/ExpressionBuilder.cs +++ b/Bonsai.Core/Expressions/ExpressionBuilder.cs @@ -765,6 +765,7 @@ class CallCandidate internal static readonly CallCandidate Ambiguous = new CallCandidate(); internal static readonly CallCandidate None = new CallCandidate(); internal MethodBase method; + internal Type declaringType; internal Expression[] arguments; internal bool generic; internal bool expansion; @@ -818,6 +819,9 @@ static CallCandidate OverloadResolution(IEnumerable methods, params return new CallCandidate { method = method, + declaringType = method.IsVirtual + ? ((MethodInfo)method).GetBaseDefinition().DeclaringType + : method.DeclaringType, arguments = callArguments, generic = method.IsGenericMethod, expansion = ParamExpansionRequired(parameters, argumentTypes), @@ -853,6 +857,15 @@ static CallCandidate OverloadResolution(IEnumerable methods, params // skip self-test if (i == j) continue; + // exclude self if declaring type is base type of other; and vice-versa + if (candidates[i].declaringType != candidates[j].declaringType) + { + if (candidates[i].declaringType.IsAssignableFrom(candidates[j].declaringType)) + candidates[i].excluded = true; + else candidates[j].excluded = true; + continue; + } + // compare implicit type conversion var comparison = CompareFunctionMember( candidateParameters[i],