Skip to content

Commit

Permalink
* Bug fixes
Browse files Browse the repository at this point in the history
- Ordering, ThenOrdering: Error if IComparer.Compare returns a number greater than 1 or smaller than -1
- Aggregating: IsDefaulted is not set to true if source collection is null
* New features
- ICanNotifyMethodChanged and ICanNotifyPropertyChanged interfaces
  • Loading branch information
IgorBuchelnikov committed Sep 1, 2021
1 parent e75fd76 commit d637549
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 72 deletions.
34 changes: 17 additions & 17 deletions src/ObservableComputations.Test/ExpressionWatcherTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public void TestRaiseValueChanged1()
bool raised = false;
Item item = new Item();
Expression<Func<bool>> expression = () => item.Num == "1";
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression));
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
item.Num = "1";
Expand All @@ -108,7 +108,7 @@ public void TestRaiseValueChanged2()
bool raised = false;
object item = new Item();
Expression<Func<bool>> expression = () => ((Item)item).Num == "1";
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression));
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
((Item)item).Num = "1";
Expand All @@ -122,7 +122,7 @@ public void TestRaiseValueChanged3()
bool raised = false;
Item item = new Item();
Expression<Func<string>> expression = () => (string)item.NumObject;
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression));
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
item.Num = "1";
Expand All @@ -137,7 +137,7 @@ public void TestRaiseValueChanged4()
Item item = new Item();
item.Num = "777";
Expression<Func<string>> expression = () => item.GetChild(item.AltNum).Num;
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression));
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
item.Num = "1";
Expand All @@ -156,7 +156,7 @@ public void TestRaiseValueChanged41()
item.Num = "777";
item.GetChild(item.AltNum).SetChild("888", new Item());
Expression<Func<string>> expression = () => item.GetChild(item.AltNum).GetChild("888").Num;
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression));
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
item.Num = "1";
Expand All @@ -176,7 +176,7 @@ public void TestRaiseValueChanged5()
item.Num = "777";
Expression<Func<string, string>> expression = n => item.GetChild(item.AltNum + n).Num;
object[] parameters = new object[]{"777"};
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression), parameters);
expressionWatcher.ParameterValues.SequenceEqual(parameters);
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
Expand All @@ -194,7 +194,7 @@ public void TestRaiseValueChanged6()
bool raised = false;
Item item = new Item();
Expression<Func<string>> expression = () => (item.Num == "777" ? item.GetChild("888") : item.GetChild("000")).Num;
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression));
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
item.Num = "777";
Expand All @@ -209,7 +209,7 @@ public void TestRaiseValueChanged61()
Item item = new Item();
item.Num = "777";
Expression<Func<string>> expression = () => (item.Num == "777" ? item.GetChild("888") : item.GetChild("000")).Num;
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression));
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
item.GetChild("888").Num = "888";
Expand All @@ -224,7 +224,7 @@ public void TestRaiseValueChanged63()
Item item = new Item();
item.Num = "777";
Expression<Func<int, string>> expression = n => (item.Num == "777" ? item.GetChild("888" + n) : item.GetChild("000")).Num;
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression), new object[]{1});
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
item.GetChild("888" + 1).Num = "888";
Expand All @@ -239,7 +239,7 @@ public void TestRaiseValueChanged64()
Item item = new Item();
item.Num = "777" + 1;
Expression<Func<int, string>> expression = n => (item.Num == "777" + n ? item.GetChild("888") : item.GetChild("000")).Num;
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression), new object[]{1});
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
item.GetChild("888").Num = "888";
Expand All @@ -254,7 +254,7 @@ public void TestRaiseValueChanged7()
bool raised = false;
Item item = new Item();
Expression<Func<string>> expression = () => item.GetChild("777").Num;
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression));
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
item.SetChild("777", new Item(){Num = "888"});
Expand All @@ -269,7 +269,7 @@ public void TestRaiseValueChanged8()
bool raised = false;
Item item = new Item();
Expression<Func<Item, string>> expression = i => i.GetChild(i.Num == "777" ? "888" : "999").Num;
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression), new object[] {item});
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
item.SetChild("000", new Item(){Num = "000"});
Expand All @@ -293,7 +293,7 @@ public void TestRaiseValueChanged9()
bool raised = false;
Item item = new Item();
Expression<Func<Item, string>> expression = i => i.GetChild("777").Num + i.GetChild("999").Num;
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression), new object[]{item});
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
item.SetChild("777", new Item(){Num = "888"});
Expand All @@ -312,7 +312,7 @@ public void TestRaiseValueChanged10()
Item item1 = new Item();
Item item2 = new Item();
Expression<Func<Item, Item, string>> expression = (i1, i2) => i1.GetChild("777").Num + i2.GetChild("999").Num;
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression), new object[]{item1, item2});
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
item1.SetChild("777", new Item(){Num = "888"});
Expand All @@ -330,7 +330,7 @@ public void TestRaiseValueChanged11()
bool raised = false;
object item = new Item();
Expression<Func<object, string>> expression = i => ((Item) i).GetChild("777").Num + ((Item) i).GetChild("999").Num;
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression), new object[]{item});
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
((Item)item).SetChild("777", new Item(){Num = "888"});
Expand All @@ -349,7 +349,7 @@ public void TestRaiseValueChanged12()
Item item1 = new Item();
Item item2 = new Item();
Expression<Func<string>> expression = () => item1.GetChild("777").Num + item2.GetChild("999").Num;
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression));
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
item1.SetChild("777", new Item(){Num = "888"});
Expand All @@ -369,7 +369,7 @@ public void TestRaiseValueChanged13()
Item item2 = new Item();
item1.Child = item2;
Expression<Func<string>> expression = () => item1.Child.Num;
ExpressionWatcher expressionWatcher = new ExpressionWatcher(
ExpressionWatcher expressionWatcher = new ExpressionWatcher(null,
ExpressionWatcher.GetExpressionInfo(expression));
expressionWatcher.ValueChanged = (ew, sender, eventArgs) => { raised = true; };
item1.Child = new Item();
Expand Down
2 changes: 1 addition & 1 deletion src/ObservableComputations/Collections/ThenOrdering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ private int getOrderedIndex(TOrderingValue orderingValue, int lowerIndex, int up

TOrderingValue middleItemOrderingValue = _orderingValues[middleIndex];

int comparisonWithMiddleItem = _comparer.Compare(orderingValue, middleItemOrderingValue);
int comparisonWithMiddleItem = Math.Sign(_comparer.Compare(orderingValue, middleItemOrderingValue));

if (comparisonWithMiddleItem == 0)
{
Expand Down
60 changes: 44 additions & 16 deletions src/ObservableComputations/Common/ExpressionWatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace ObservableComputations
{
internal sealed class ExpressionWatcher
{
IComputing _owner;
internal Position _position;
internal readonly PropertyChangedEventSubscription[] _propertyChangedEventSubscriptions;
internal readonly MethodChangedEventSubscription[] _methodChangedEventSubscriptions;
Expand Down Expand Up @@ -318,8 +319,9 @@ private void initialize(ExpressionInfo expressionInfo)
if (expressionInfo._constantCallTrees != null) workWithCallTrees(expressionInfo._constantCallTrees);
}

internal ExpressionWatcher(ExpressionInfo expressionInfo)
internal ExpressionWatcher(IComputing owner, ExpressionInfo expressionInfo)
{
_owner = owner;
ExpressionToWatch = expressionInfo._expressionToWatch;
_parameterValues = null;
_propertyChangedEventSubscriptions = new PropertyChangedEventSubscription[expressionInfo._callCount];
Expand Down Expand Up @@ -348,8 +350,9 @@ private ExpressionWatcher(object[] parameterValues, ExpressionInfo expressionInf
initialize(expressionInfo);
}

public ExpressionWatcher(ExpressionInfo expressionInfo, object[] parameters)
public ExpressionWatcher(IComputing owner, ExpressionInfo expressionInfo, object[] parameters)
{
_owner = owner;
ExpressionToWatch = expressionInfo._expressionToWatch;
_parameterValues = parameters;
_propertyChangedEventSubscriptions = new PropertyChangedEventSubscription[expressionInfo._callCount];
Expand Down Expand Up @@ -735,16 +738,18 @@ private void workWithCallTreeNode(CallTreeNode node, object holder, WorkWithCall
_currentComputings[callIndex] = newComputingInternal;
}


string memberName = node._call.Name;
switch (node._call.Type)
{
case CallType.PropertyOrField:


if (node._holder is INotifyPropertyChanged notifyPropertyChanged)
void subscribePropertyChanged(INotifyPropertyChanged notifyPropertyChanged)
{
string propertyName = node._call.Name;
node._propertyChangedEventHandler = (sender, args) =>
{
if (!_disposed && args.PropertyName == propertyName)
if (!_disposed && args.PropertyName == memberName)
#if DEBUG
processChange(node, root, sender, args);
#else
Expand All @@ -753,21 +758,34 @@ private void workWithCallTreeNode(CallTreeNode node, object holder, WorkWithCall
};

notifyPropertyChanged.PropertyChanged += node._propertyChangedEventHandler;
_propertyChangedEventSubscriptions[callIndex] = new PropertyChangedEventSubscription(notifyPropertyChanged, node._propertyChangedEventHandler);
_propertyChangedEventSubscriptions[callIndex] =
new PropertyChangedEventSubscription(notifyPropertyChanged, node._propertyChangedEventHandler);
}


if (node._holder is ICanNotifyPropertyChanged canNotifyPropertyChanged)
{
if (canNotifyPropertyChanged.CanNotifyPropertyChanged(memberName, _rootExpressionWatcher._owner))
subscribePropertyChanged(canNotifyPropertyChanged);
}
else if (node._holder is INotifyPropertyChanged notifyPropertyChanged)
{
subscribePropertyChanged(notifyPropertyChanged);
}
break;
case CallType.Method:
if (node._holder is INotifyMethodChanged notifyMethodChanged)
int argumentsCount = node._call.GetArgumentValues.Length;

void subscribeMethodChanged(INotifyMethodChanged notifyMethodChanged)
{
CallTreeNode nodeCopy = node;
node._methodChangedEventHandler = (sender, args) =>
{
if (!_disposed && args.MethodName == nodeCopy._call.Name)
if (!_disposed && args.MethodName == memberName)
{
int length = nodeCopy._call.GetArgumentValues.Length;
object[] argumentValues = new object[length];
for (int index = 0; index < length; index++)
argumentValues[index] = nodeCopy._call.GetArgumentValues[index].DynamicInvoke(_parameterValues);

object[] argumentValues = new object[argumentsCount];
for (int index = 0; index < argumentsCount; index++)
argumentValues[index] = node._call.GetArgumentValues[index].DynamicInvoke(_parameterValues);

if (args.ArgumentsPredicate(argumentValues))
#if DEBUG
Expand All @@ -776,11 +794,21 @@ private void workWithCallTreeNode(CallTreeNode node, object holder, WorkWithCall
processChange(node, sender, args);
#endif
}

};

notifyMethodChanged.MethodChanged += node._methodChangedEventHandler;
_methodChangedEventSubscriptions[callIndex] = new MethodChangedEventSubscription(notifyMethodChanged, node._methodChangedEventHandler);
_methodChangedEventSubscriptions[callIndex] =
new MethodChangedEventSubscription(notifyMethodChanged, node._methodChangedEventHandler);
}

if (node._holder is ICanNotifyMethodChanged canNotifyMethodChanged)
{
if (canNotifyMethodChanged.CanNotifyMethodChanged(memberName, argumentsCount, _rootExpressionWatcher._owner))
subscribeMethodChanged(canNotifyMethodChanged);
}
else if (node._holder is INotifyMethodChanged notifyMethodChanged)
{
subscribeMethodChanged(notifyMethodChanged);
}

ExpressionWatcher[] nodeCallArguments = node._callArguments;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.ComponentModel;

namespace ObservableComputations
{
public interface ICanNotifyMethodChanged : INotifyMethodChanged
{
bool CanNotifyMethodChanged(string methodName, int argumentsCount, IComputing computing);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.ComponentModel;

namespace ObservableComputations
{
public interface ICanNotifyPropertyChanged : INotifyPropertyChanged
{
bool CanNotifyPropertyChanged(string propertyName, IComputing computing);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ internal interface ISourceIndexerPropertyTracker

internal interface ILeftSourceIndexerPropertyTracker : ISourceIndexerPropertyTracker
{
void HandleSourcePropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs);
new void HandleSourcePropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs);
}

internal interface IRightSourceIndexerPropertyTracker : ISourceIndexerPropertyTracker
{
void HandleSourcePropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs);
new void HandleSourcePropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ internal interface ISourceItemChangeProcessor : IComputingInternal

internal interface ISourceItemKeyChangeProcessor : ISourceItemChangeProcessor
{
void ProcessSourceItemChange(ExpressionWatcher expressionWatcher);
new void ProcessSourceItemChange(ExpressionWatcher expressionWatcher);
}

internal interface ISourceItemValueChangeProcessor : ISourceItemChangeProcessor
{
void ProcessSourceItemChange(ExpressionWatcher expressionWatcher);
new void ProcessSourceItemChange(ExpressionWatcher expressionWatcher);
}
}
4 changes: 2 additions & 2 deletions src/ObservableComputations/Common/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ internal static void getItemInfoContent<TExpression, TExpressionCompiled>(object
{
if (!expressionContainsParametrizedLiveLinqCalls)
{
watcher = new ExpressionWatcher(valueSelectorExpressionInfo, sourceItems);
watcher = new ExpressionWatcher(current, valueSelectorExpressionInfo, sourceItems);
func = default(TExpressionCompiled);
nestedComputings = null;
expressionCallCount = valueSelectorExpressionInfo._callCount;
Expand All @@ -638,7 +638,7 @@ internal static void getItemInfoContent<TExpression, TExpressionCompiled>(object

ExpressionWatcher.ExpressionInfo expressionInfo =
ExpressionWatcher.GetExpressionInfo(predicateExpression);
watcher = new ExpressionWatcher(expressionInfo);
watcher = new ExpressionWatcher(current, expressionInfo);

expressionCallCount = expressionInfo._callCount;
}
Expand Down
15 changes: 7 additions & 8 deletions src/ObservableComputations/ObservableComputations.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,17 @@
<NeutralLanguage>en</NeutralLanguage>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageReleaseNotes>* New features
- specifying a default value for the all scalar operators (reduces verbosity)
- CollectionItemProcessing (reduces verbosity)

* Renamed
— CollectionProcessing -&gt; CollectionItemsProcessing</PackageReleaseNotes>
<PackageReleaseNotes>* Bug fixes
- Ordering, ThenOrdering: Error if IComparer.Compare returns a number greater than 1 or smaller than -1
- Aggregating: IsDefaulted is not set to true if source collection is null
* New features
- ICanNotifyMethodChanged and ICanNotifyPropertyChanged interfaces</PackageReleaseNotes>
<Version>2.2.0</Version>
<AssemblyName>ObservableComputations</AssemblyName>
<PackageId>ObservableComputations</PackageId>
<Product>ObservableComputations</Product>
<AssemblyVersion>2.2.0.0</AssemblyVersion>
<FileVersion>2.2.0.0</FileVersion>
<AssemblyVersion>2.2.1.0</AssemblyVersion>
<FileVersion>2.2.1.0</FileVersion>
<PackageIconUrl></PackageIconUrl>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<Copyright>Igor Buchelnikov (c)</Copyright>
Expand Down
Loading

0 comments on commit d637549

Please sign in to comment.