I use 2 Timers, 1 Check all my Citect tags for changed values each second. The other one is a KeepAlive Timer for a TCP Connection.
This is how i call the Timers:
Timer timer = new Timer(ctApiCtrl.CheckUpdateAllInputTags, "updatetags", 1000, 1000);
Timer timer2 = new Timer(mepCtrl.KeepAlive, "test", 3000, 3000);
Now due the nature of how the existing systems works together is with writing and reading PLC/DSK tags. So if my service wants to detect these changes it needs to periodically "poll" or check these values.
This is the class where those tags comes from:
public class TagWithValue : INotifyPropertyChanged
{
public string TagName { get; set; }
private string tagValue;
public TagCategory TagCategory { get; set; }
public string TagValue
{
get { return tagValue; }
set
{
if(tagValue != value)
{
tagValue = value;
RaisePropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] string prop = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
So when the property TagValue changes it detects it and fire x.
public void CheckUpdateAllInputTags(object objectInfo)
{
VDGSenseController.Authenticate();
if (listOfTags.Video == null || listOfTags.Audio == null)
{
Logger.Info("Empty Tag list , reinitliazing all tags");
InitiliazeAllTags();
}
foreach (var tag in Tags)
{
try
{
var value = TagRead(tag.TagName); //read Tag value
tag.TagValue = value;
tag.PropertyChanged += d_PropertyChanged;
}
catch (Exception ex)
{
Logger.Warn(ex,"Importing tag unsuccesfull");
continue;
}
}
Tags = Tags.Where(t => !string.IsNullOrWhiteSpace(t.TagValue)).Distinct().ToList();
}
Here lies the problem of this snippet.
private void d_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
//trigger wanneer prop value is changed
var tagchanged = (CtModel.TagWithValue)sender;
Logger.Info($"Tag: {tagchanged.TagName} with value: {tagchanged.TagValue} has changed.");
}
The problem i have with this code is that it fires multiple times the propertychanged method in the same timer tick.
Note the logging that contains Tag: DK_OM_2A
How can i change this so it only fires once each timer tick?
I fixed it by moving the logic from the propertychanged in a different method and in the new code it goes like this:
private void d_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
//trigger wanneer prop value is changed
var tagchanged = (CtModel.TagWithValue)sender;
tagsWithChangedValue.Add(tagchanged);
tagsWithChangedValue = tagsWithChangedValue.Distinct().ToList();
}
#region UpdateTagsPropertyChanged
public void UpdateTagsPropertyChanged(List<CtModel.TagWithValue> listofchangedtags)
{
if(listofchangedtags.Count == 0)
{
return; // if list is empty, go back
}
foreach(var tagchanged in listofchangedtags)
{
logger.Info($"Tag: {tagchanged.TagName} with value: {tagchanged.TagValue} has changed.");
switch (tagchanged.TagName)
{
default:
break;
}
}
tagsWithChangedValue.Clear(); //clears list for the next cycle
}
#endregion
I have completed the inventory slots already and want to lock and unlock some slots for extending after ....(which press m for testing)
I press M and unlock slot increasing now but cannot use extended slots after increase the number of unlock slot.
Have completed all inventory slots and item to move among slots
public abstract class ItemContainer : MonoBehaviour, IItemContainer
{
public List<ItemSlot> ItemSlots;
public int unlockSlot = 1;
public event Action<BaseItemSlot> OnPointerEnterEvent;
public event Action<BaseItemSlot> OnPointerExitEvent;
public event Action<BaseItemSlot> OnRightClickEvent;
public event Action<BaseItemSlot> OnBeginDragEvent;
public event Action<BaseItemSlot> OnEndDragEvent;
public event Action<BaseItemSlot> OnDragEvent;
public event Action<BaseItemSlot> OnDropEvent;
protected virtual void OnValidate()
{
GetComponentsInChildren(includeInactive: true, result: ItemSlots);
}
protected virtual void Start()
{
for (int i = 0; i < ItemSlots.Count/5* unlockSlot; i++)
{
ItemSlots[i].OnPointerEnterEvent += slot => EventHelper(slot, OnPointerEnterEvent);
ItemSlots[i].OnPointerExitEvent += slot => EventHelper(slot, OnPointerExitEvent);
ItemSlots[i].OnRightClickEvent += slot => EventHelper(slot, OnRightClickEvent);
ItemSlots[i].OnBeginDragEvent += slot => EventHelper(slot, OnBeginDragEvent);
ItemSlots[i].OnEndDragEvent += slot => EventHelper(slot, OnEndDragEvent);
ItemSlots[i].OnDragEvent += slot => EventHelper(slot, OnDragEvent);
ItemSlots[i].OnDropEvent += slot => EventHelper(slot, OnDropEvent);
}
}
private void EventHelper(BaseItemSlot itemSlot, Action<BaseItemSlot> action)
{
if (action != null)
action(itemSlot);
}
public virtual bool CanAddItem(Item item, int amount = 1)
{
int freeSpaces = 0;
foreach (ItemSlot itemSlot in ItemSlots)
{
if (itemSlot.Item == null || itemSlot.Item.ID == item.ID)
{
freeSpaces += item.MaxStacks - itemSlot.Amount;
}
}
return freeSpaces >= amount;
}
public virtual bool AddItem(Item item)
{
for (int i = 0; i < ItemSlots.Count; i++)
{
if (ItemSlots[i].CanAddStack(item))
{
ItemSlots[i].Item = item;
ItemSlots[i].Amount++;
return true;
}
}
for (int i = 0; i < ItemSlots.Count; i++)
{
if (ItemSlots[i].Item == null)
{
if (item.ID == item.ID)
{
ItemSlots[i].Item = item;
ItemSlots[i].Amount++;
return true;
}
}
}
return false;
}
public virtual bool RemoveItem(Item item)
{
for (int i = 0; i < ItemSlots.Count; i++)
{
if (ItemSlots[i].Item == item)
{
ItemSlots[i].Amount--;
return true;
}
}
return false;
}
public virtual Item RemoveItem(string itemID)
{
for (int i = 0; i < ItemSlots.Count; i++)
{
Item item = ItemSlots[i].Item;
if (item != null && item.ID == itemID)
{
ItemSlots[i].Amount--;
return item;
}
}
return null;
}
public virtual int ItemCount(string itemID)
{
int number = 0;
for (int i = 0; i < ItemSlots.Count; i++)
{
Item item = ItemSlots[i].Item;
if (item != null && item.ID == itemID)
{
number += ItemSlots[i].Amount;
}
}
return number;
}
private void Update()
{
if (Input.GetKeyUp(KeyCode.M))
{
unlockSlot++;
Debug.Log(unlockSlot);
}
}
}
unlock slot not increase (debug not showing) and the rest slots disable to use
Fixed: just duplicate start function to fixedupdate.
DON'T!
This sounds very bad for resources ... why setting this all the time? Rather move it to the moment in which you actually change unlockSlot and do it there only once.
Additionally you do += with a lambda expression. So the events will be added additional incremental so next time the callback is executed twice .. then 3 times etc.
And since it is a lambda expression you also can't use -= in order to remove the callback.
In order to automatize it a bit I would use a property with backing field like
private int unlockSlot = 1;
public int UnlockSlot
{
get
{
return unlockSlot;
}
// you can additionally make the setter private so only this class
// can change the value while other classes have only read permissions
/*private*/
set
{
unlockSlot = value;
UpdateItemSlots();
}
}
Then you can avoid your EventHelper .. this can simply be written as
private void UpdateItemSlots()
{
for (var i = 0; i < ItemSlots.Count / 5 * unlockSlot; i++)
{
// Before adding callbacks remove them
// this is valid even if they were not added before
// but makes sure they are added only exactly once
ItemSlots[i].OnPointerEnterEvent -= OnPointerEnterEvent;
ItemSlots[i].OnPointerExitEvent -= OnPointerExitEvent;
ItemSlots[i].OnRightClickEvent -= OnRightClickEvent;
ItemSlots[i].OnBeginDragEvent -= OnBeginDragEvent;
ItemSlots[i].OnEndDragEvent -= OnEndDragEvent;
ItemSlots[i].OnDragEvent -= OnDragEvent;
ItemSlots[i].OnDropEvent -= OnDropEvent;
// add your callback events .. no need for this complex lambda construct
ItemSlots[i].OnPointerEnterEvent += OnPointerEnterEvent;
ItemSlots[i].OnPointerExitEvent += OnPointerExitEvent;
ItemSlots[i].OnRightClickEvent += OnRightClickEvent;
ItemSlots[i].OnBeginDragEvent += OnBeginDragEvent;
ItemSlots[i].OnEndDragEvent += OnEndDragEvent;
ItemSlots[i].OnDragEvent += OnDragEvent;
ItemSlots[i].OnDropEvent += OnDropEvent;
}
}
Afaik you don't need the null check for event Action you could go for sure and declare them in a way they are never null like e.g.
public event Action<BaseItemSlot> OnPointerEnterEvent = slot => {};
this adds an empty callback which is as said before not removable due to being a lambda expression => it is assured that OnPointerEnterEvent will never be null.
Now simply change
protected virtual void Start()
{
UpdateItemSlots();
}
and remember to not use unlockSlot but rather UnlockSlot for changing its value so also the slots get updated automatically by the property
private void Update()
{
if (Input.GetKeyUp(KeyCode.M))
{
UnlockSlot++;
// for reading in this case it doesn't matter
// but for being consequent I would also use the property here
Debug.Log(UnlockSlot);
}
}
I have a class STimer that has a List in it. The serviceDetail is monitored on a timer very often, and rarely changes, but when it does change I want to get the fact that the data changed without looping through the list due to processing power. Maybe this is a duplicate question that I just don't know how to search for it, but I have been trying. Here is a code Sample:
class STimers
{
public class ServiceDetail
{
private int _serviceKey;
private bool _isRunning = true;
private bool _runningStateChanged = false;
public bool isRunning
{
get { return _isRunning; }
set
{
//Check to see if the data is the same, if so, don't change, if not, change and flag as changed
if(_isRunning = value) { return; }
else
{
_isRunning = value;
_runningStateChanged = true;
<-- Update STimers._dataChanged to true -->
}
}
}
}
public List<ServiceDetail> _serviceMonitors = new List<ServiceDetail>();
public bool _dataChanged = false;
}
I could do a .Find on the list to return all of the _serviceMonitors._runningStateChanged=true, but that seems like a lot of work parsing the List every time the timer fires, when likely only 1 out of 1,000 loops will actually have a change.
Is this even possible, or do I need to move the check for changes out of the class?
You could get this by adding an event to your ServiceDetail class
public class ServiceDetail
{
public event EventHandler<ListChangedEventArgs> ListChanged;
private int _serviceKey;
private bool _isRunning = true;
private bool _runningStateChanged = false;
private void OnListChanged(ListChangedEventArgs e){
if (ListChanged != null) ListChanged(this, e);
}
public bool isRunning
{
get { return _isRunning; }
set
{
//Check to see if the data is the same, if so, don't change, if not, change and flag as changed
if(_isRunning = value) { return; }
else
{
_isRunning = value;
_runningStateChanged = true;
OnListChanged(new ListChangedEventArgs(this));
<-- Update STimers._dataChanged to true -->
}
}
}
}
And define your ListChangedEventArgs class like this
public class ListChangedEventArgs:EventArgs
{
public ServiceDetail serviceDetail { get; set; }
public ListChangedEventArgs(ServiceDetail s)
{
serviceDetail = s;
}
}
And then register to the event for each servicedetail added to the list
s.ListChanged += (sender, args) => YourFunction();
Hope it helps
I have a helper method for my unit tests that asserts that a specific sequence of events were raised in a specific order. The code is as follows:
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction)
{
var expectedSequence = new Queue<int>();
for (int i = 0; i < subscribeActions.Count; i++)
{
expectedSequence.Enqueue(i);
}
ExpectEventSequence(subscribeActions, triggerAction, expectedSequence);
}
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence)
{
var fired = new Queue<int>();
var actionsCount = subscribeActions.Count;
for(var i =0; i< actionsCount;i++)
{
subscription((o, e) =>
{
fired.Enqueue(i);
});
}
triggerAction();
var executionIndex = 0;
var inOrder = true;
foreach (var firedIndex in fired)
{
if (firedIndex != expectedSequence.Dequeue())
{
inOrder = false;
break;
}
executionIndex++;
}
if (subscribeActions.Count != fired.Count)
{
Assert.Fail("Not all events were fired.");
}
if (!inOrder)
{
Assert.Fail(string.Format(
CultureInfo.CurrentCulture,
"Events were not fired in the expected sequence from element {0}",
executionIndex));
}
}
Example usage is as follows:
[Test()]
public void FillFuel_Test([Values(1, 5, 10, 100)]float maxFuel)
{
var fuelTank = new FuelTank()
{
MaxFuel = maxFuel
};
var eventHandlerSequence = new Queue<Action<EventHandler>>();
eventHandlerSequence.Enqueue(x => fuelTank.FuelFull += x);
//Dealing with a subclass of EventHandler
eventHandlerSequence.Enqueue(x => fuelTank.FuelChanged += (o, e) => x(o, e));
Test.ExpectEventSequence(eventHandlerSequence, () => fuelTank.FillFuel());
}
And the code under test:
public float Fuel
{
get
{
return fuel;
}
private set
{
var adjustedFuel = Math.Max(0, Math.Min(value, MaxFuel));
if (fuel != adjustedFuel)
{
var oldFuel = fuel;
fuel = adjustedFuel;
RaiseCheckFuelChangedEvents(oldFuel);
}
}
}
public void FillFuel()
{
Fuel = MaxFuel;
}
private void RaiseCheckFuelChangedEvents(float oldFuel)
{
FuelChanged.FireEvent(this, new FuelEventArgs(oldFuel, Fuel));
if (fuel == 0)
{
FuelEmpty.FireEvent(this, EventArgs.Empty);
}
else if (fuel == MaxFuel)
{
FuelFull.FireEvent(this, EventArgs.Empty);
}
if (oldFuel == 0 && Fuel != 0)
{
FuelNoLongerEmpty.FireEvent(this, EventArgs.Empty);
}
else if (oldFuel == MaxFuel && Fuel != MaxFuel)
{
FuelNoLongerFull.FireEvent(this, EventArgs.Empty);
}
}
So the test expects FuelFilled to be fired before FuelChanged but in actuality FuelChanged is fired first, which fails the test.
However my test is instead reporting that FuelChanged is being fired twice, but when I step through the code it is clear that FuelFilled is fired after FuelChanged and FuelChanged is only fired once.
I assumed that it was something to do with the way lambdas work with local state, maybe the for loop iterator variable was only ever set to the final value, so I replaced the for loop with this:
var subscriptions = subscribeActions.ToList();
foreach (var subscription in subscriptions)
{
subscription((o, e) =>
{
var index = subscriptions.IndexOf(subscription);
fired.Enqueue(index);
});
}
However the result is the same, fired contains {1;1} instead of {1;0}.
Now I'm wondering if the same lambda is being assigned to both events instead of using the different subscription / index state. Any ideas?
Update: I was unable to get success with either answer posted so far (same as my initial results), despite their similarities to my actual code, so I presume the issue is located elsewhere in my FuelTank code. I've pasted the full code for FuelTank below:
public class FuelTank
{
public FuelTank()
{
}
public FuelTank(float initialFuel, float maxFuel)
{
MaxFuel = maxFuel;
Fuel = initialFuel;
}
public float Fuel
{
get
{
return fuel;
}
private set
{
var adjustedFuel = Math.Max(0, Math.Min(value, MaxFuel));
if (fuel != adjustedFuel)
{
var oldFuel = fuel;
fuel = adjustedFuel;
RaiseCheckFuelChangedEvents(oldFuel);
}
}
}
private float maxFuel;
public float MaxFuel
{
get
{
return maxFuel;
}
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("MaxFuel", value, "Argument must be not be less than 0.");
}
maxFuel = value;
}
}
private float fuel;
public event EventHandler<FuelEventArgs> FuelChanged;
public event EventHandler FuelEmpty;
public event EventHandler FuelFull;
public event EventHandler FuelNoLongerEmpty;
public event EventHandler FuelNoLongerFull;
public void AddFuel(float fuel)
{
Fuel += fuel;
}
public void ClearFuel()
{
Fuel = 0;
}
public void DrainFuel(float fuel)
{
Fuel -= fuel;
}
public void FillFuel()
{
Fuel = MaxFuel;
}
private void RaiseCheckFuelChangedEvents(float oldFuel)
{
FuelChanged.FireEvent(this, new FuelEventArgs(oldFuel, Fuel));
if (fuel == 0)
{
FuelEmpty.FireEvent(this, EventArgs.Empty);
}
else if (fuel == MaxFuel)
{
FuelFull.FireEvent(this, EventArgs.Empty);
}
if (oldFuel == 0 && Fuel != 0)
{
FuelNoLongerEmpty.FireEvent(this, EventArgs.Empty);
}
else if (oldFuel == MaxFuel && Fuel != MaxFuel)
{
FuelNoLongerFull.FireEvent(this, EventArgs.Empty);
}
}
}
FuelEventArgs looks like this:
public class FuelEventArgs : EventArgs
{
public float NewFuel
{
get;
private set;
}
public float OldFuel
{
get;
private set;
}
public FuelEventArgs(float oldFuel, float newFuel)
{
this.OldFuel = oldFuel;
this.NewFuel = newFuel;
}
}
The FireEvent extension method is looks like this:
public static class EventHandlerExtensions
{
/// <summary>
/// Fires the event. This method is thread safe.
/// </summary>
/// <param name="handler"> The handler. </param>
/// <param name="sender"> Source of the event. </param>
/// <param name="args"> The <see cref="EventArgs"/> instance containing the event data. </param>
public static void FireEvent(this EventHandler handler, object sender, EventArgs args)
{
var handlerCopy = handler;
if (handlerCopy != null)
{
handlerCopy(sender, args);
}
}
/// <summary>
/// Fires the event. This method is thread safe.
/// </summary>
/// <typeparam name="T"> The type of event args this handler has. </typeparam>
/// <param name="handler"> The handler. </param>
/// <param name="sender"> Source of the event. </param>
/// <param name="args"> The <see cref="EventArgs"/> instance containing the event data. </param>
public static void FireEvent<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs
{
var handlerCopy = handler;
if (handlerCopy != null)
{
handlerCopy(sender, args);
}
}
}
The full test code can be found above in the question, there is no other code called during test execution.
I am using NUnit test framework via the Unity Testing Tools plugin for the Unity3D engine, .NET version 3.5 (ish, it's closer to Mono 2.0, I believe), and Visual Studio 2013.
Update 2:
After extracting the code and tests to their own project (outside of the Unity3D ecosystem) all tests run as expected, so I'm going to have to chalk this one up to a bug in the Unity -> Visual Studio bridge.
I have the following implementation based upon Nick's question.
First the class for the FuelTank:
public class FuelTank
{
private float fuel;
//Basic classes for the event handling, could be done by providing a few simple delegates,
//but this is just to stick as close to the original question as possible.
public FuelChanged FuelChanged = new FuelChanged();
public FuelEmpty FuelEmpty = new FuelEmpty();
public FuelFull FuelFull = new FuelFull();
public FuelNoLongerEmpty FuelNoLongerEmpty = new FuelNoLongerEmpty();
public FuelNoLongerFull FuelNoLongerFull = new FuelNoLongerFull();
public float MaxFuel { get; set; }
public float Fuel
{
get
{
return fuel;
}
private set
{
var adjustedFuel = Math.Max(0, Math.Min(value, MaxFuel));
if (fuel != adjustedFuel)
{
var oldFuel = fuel;
fuel = adjustedFuel;
RaiseCheckFuelChangedEvents(oldFuel);
}
}
}
public void FillFuel()
{
Fuel = MaxFuel;
}
private void RaiseCheckFuelChangedEvents(float oldFuel)
{
FuelChanged.FireEvent(this, new FuelEventArgs(oldFuel, Fuel));
if (fuel == 0)
{
FuelEmpty.FireEvent(this, EventArgs.Empty);
}
else if (fuel == MaxFuel)
{
FuelFull.FireEvent(this, EventArgs.Empty);
}
if (oldFuel == 0 && Fuel != 0)
{
FuelNoLongerEmpty.FireEvent(this, EventArgs.Empty);
}
else if (oldFuel == MaxFuel && Fuel != MaxFuel)
{
FuelNoLongerFull.FireEvent(this, EventArgs.Empty);
}
}
}
As the code for the event handlers was missing, I made an assumption to use this. As the comment describes in the previous code block, it could be done more easily with plain delegates. It's just a matter of choice, for which I think this implementation isn't the best yet, but suitable enough for debugging:
public class FuelEventArgs : EventArgs
{
private float oldFuel, newFuel;
public FuelEventArgs(float oldFuel, float newFuel)
{
this.oldFuel = oldFuel;
this.newFuel = newFuel;
}
}
public class FuelEvents
{
public event EventHandler FireEventHandler;
public virtual void FireEvent(object sender, EventArgs fuelArgs)
{
EventHandler handler = FireEventHandler;
if (null != handler)
handler(this, fuelArgs);
}
}
public class FuelChanged : FuelEvents
{
public override void FireEvent(object sender, EventArgs fuelArgs)
{
Console.WriteLine("Fired FuelChanged");
base.FireEvent(sender, fuelArgs);
}
}
public class FuelEmpty : FuelEvents
{
public override void FireEvent(object sender, EventArgs fuelArgs)
{
Console.WriteLine("Fired FuelEmpty");
base.FireEvent(sender, fuelArgs);
}
}
public class FuelFull : FuelEvents
{
public override void FireEvent(object sender, EventArgs fuelArgs)
{
Console.WriteLine("Fired FuelFull");
base.FireEvent(sender, fuelArgs);
}
}
public class FuelNoLongerEmpty : FuelEvents
{
public override void FireEvent(object sender, EventArgs fuelArgs)
{
Console.WriteLine("Fired FuelNoLongerEmpty");
base.FireEvent(sender, fuelArgs);
}
}
public class FuelNoLongerFull : FuelEvents
{
public override void FireEvent(object sender, EventArgs fuelArgs)
{
Console.WriteLine("Fired FuelNoLongerFull");
base.FireEvent(sender, fuelArgs);
}
}
And to test it all, I used this class, containing most code from the original question:
[TestFixture]
public class Tests
{
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction)
{
var expectedSequence = new Queue<int>();
for (int i = 0; i < subscribeActions.Count; i++)
{
expectedSequence.Enqueue(i);
}
ExpectEventSequence(subscribeActions, triggerAction, expectedSequence);
}
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence)
{
var fired = new Queue<int>();
var actionsCount = subscribeActions.Count;
//This code has been commented out due to the fact that subscription is unknown here.
//I stuck to use the last solution that Nick provided himself
//for (var i = 0; i < actionsCount; i++)
//{
// subscription((o, e) =>
// {
// fired.Enqueue(i);
// });
//}
var subscriptions = subscribeActions.ToList();
foreach (var subscription in subscriptions)
{
subscription((o, e) =>
{
var index = subscriptions.IndexOf(subscription);
Console.WriteLine("[ExpectEventSequence] Found index: {0}", index);
fired.Enqueue(index);
});
}
triggerAction();
var executionIndex = 0;
var inOrder = true;
foreach (var firedIndex in fired)
{
if (firedIndex != expectedSequence.Dequeue())
{
inOrder = false;
break;
}
executionIndex++;
Console.WriteLine("Execution index: {0}", executionIndex);
}
if (subscribeActions.Count != fired.Count)
{
Assert.Fail("Not all events were fired.");
}
if (!inOrder)
{
Console.WriteLine("Contents of Fired Queue: {0}", PrintValues(fired));
Assert.Fail(string.Format(
CultureInfo.CurrentCulture,
"Events were not fired in the expected sequence from element {0}",
executionIndex));
}
}
private static string PrintValues(Queue<int> myCollection)
{
return string.Format( "{{0}}", string.Join(",", myCollection.ToArray()));
}
[Test()]
[ExpectedException(typeof(DivideByZeroException))]
public void FillFuel_Test([Values(1, 5, 10, 100)]float maxFuel)
{
var fuelTank = new FuelTank()
{
MaxFuel = maxFuel
};
var eventHandlerSequence = new Queue<Action<EventHandler>>();
eventHandlerSequence.Enqueue(x => fuelTank.FuelFull.FireEventHandler += x);
//Dealing with a subclass of EventHandler
eventHandlerSequence.Enqueue(x => fuelTank.FuelChanged.FireEventHandler += (o, e) => x(o, e));
ExpectEventSequence(eventHandlerSequence, () => fuelTank.FillFuel());
}
}
Now, when running the tests with NUnit, I noticed the following results:
The first event that got triggered was the FuelChanged event, this causes the fired queue within the method
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence)
to contain {1}.
The next event that triggers is the FuelFull event, which means that the fired queue now contains:
{1,0} as expected according to the question of Nick.
The last event that triggers is the FuelNoLongerEmpty event and this one fails the test.
Note:
As this code does not yet provide an answer to the original question of the fact that lambda's might cause some interference, as the code I provided above, does just the right thing.
The following rules apply to variable scope in lambda expressions:
A variable that is captured will not be garbage-collected until the
delegate that references it goes out of scope.
Variables introduced within a lambda expression are not visible in
the outer method.
A lambda expression cannot directly capture a ref or out parameter
from an enclosing method.
A return statement in a lambda expression does not cause the
enclosing method to return.
A lambda expression cannot contain a goto statement, break statement,
or continue statement whose target is outside the body or in the body
of a contained anonymous function.
So the problem in Nick's original question might be caused by the fact that you enumerate over a queue. While enumerating and passing these directly to a lambda expression, you will work with a reference. A trick might be to actually de-reference it by copying it to a local variable within the scope of the iteration loop. This is exactly what smiech is referring to in his post.
EDIT:
I've just looked into it again for you. Are you sure the 'challenge' that you are having is not just the fact that comparing the fired dictionary's indices to the expectedSequence.Dequeue is happening in reversed order? Please note that queue's are FIFO based, so when dequeueing, it will retrieve the first that is inserted...
I noticed that (according to my code) the fired dictionary contains {1,0} whereas the expectedSequence dictionary contains {0,1}. By looking at the expected events, this is good for the expectedSequence queue. So actually the fired queue (filled within your last code block) is built up incorrectly by means of the 'age' of the eventhandler.
When I change one statement within the code that you provided in the original
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence)
method from
var subscriptions = subscribeActions.ToList();
foreach (var firedIndex in fired)
{
if (firedIndex != expectedSequence.Dequeue())
{
inOrder = false;
break;
}
executionIndex++;
Console.WriteLine("Execution index: {0}", executionIndex);
}
to this:
//When comparing indexes, you'll probably need to reverse the fired queue
fired = new Queue<int>(fired.Reverse());
foreach (var firedIndex in fired)
{
if (firedIndex != expectedSequence.Dequeue())
{
inOrder = false;
break;
}
executionIndex++;
Console.WriteLine("Execution index: {0}", executionIndex);
}
then everything in your test will pass flawlessly, as you can see with this screenshot:
for the first part: yes, it had to do with the way lambdas variable scope. See Access to Modified Closure.
Because I spent some time trying to figure it out, I allow myself to paste the code I've used (all tests passing).
class Test
{
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction)
{
var expectedSequence = new Queue<int>();
for (int i = 0; i < subscribeActions.Count; i++)
expectedSequence.Enqueue(i);
ExpectEventSequence(subscribeActions, triggerAction, expectedSequence);
}
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence)
{
var fired = new Queue<int>();
var subscriptions = subscribeActions.ToList();
foreach (var subscription in subscriptions)
{
subscription((o, e) =>
{
var index = subscriptions.IndexOf(subscription);
fired.Enqueue(index);
});
}
triggerAction();
var executionIndex = 0;
var inOrder = true;
foreach (var firedIndex in fired)
{
if (firedIndex != expectedSequence.Dequeue())
{
inOrder = false;
break;
}
executionIndex++;
}
if (subscribeActions.Count != fired.Count)
Assert.Fail("Not all events were fired.");
if (!inOrder)
Assert
.Fail(string.Format(
CultureInfo.CurrentCulture,
"Events were not fired in the expected sequence from element {0}",
executionIndex));
}
}
public class Fueled
{
public event EventHandler<FuelEventArgs> FuelChanged = delegate { };
public event EventHandler FuelEmpty = delegate { };
public event EventHandler FuelFull = delegate { };
public event EventHandler FuelNoLongerFull = delegate { };
public event EventHandler FuelNoLongerEmpty = delegate { };
private float fuel;
public float Fuel
{
get{ return fuel; }
private set
{
var adjustedFuel = Math.Max(0, Math.Min(value, MaxFuel));
if (fuel != adjustedFuel)
{
var oldFuel = fuel;
fuel = adjustedFuel;
RaiseCheckFuelChangedEvents(oldFuel);
}
}
}
public void FillFuel()
{
Fuel = MaxFuel;
}
public float MaxFuel { get; set; }
private void RaiseCheckFuelChangedEvents(float oldFuel)
{
FuelChanged(this, new FuelEventArgs(oldFuel, Fuel));
if (fuel == 0)
FuelEmpty(this, EventArgs.Empty);
else if (fuel == MaxFuel)
FuelFull(this, EventArgs.Empty);
if (oldFuel == 0 && Fuel != 0)
FuelNoLongerEmpty(this, EventArgs.Empty);
else if (oldFuel == MaxFuel && Fuel != MaxFuel)
FuelNoLongerFull(this, EventArgs.Empty);
}
}
public class FuelEventArgs : EventArgs
{
public FuelEventArgs(float oldFuel, float fuel)
{
}
}
[TestFixture]
public class Tests
{
[Test()]
public void FillFuel_Test([Values(1, 5, 10, 100)]float maxFuel)
{
var fuelTank = new Fueled()
{
MaxFuel = maxFuel
};
var eventHandlerSequence = new Queue<Action<EventHandler>>();
//Dealing with a subclass of EventHandler
eventHandlerSequence.Enqueue(x => fuelTank.FuelChanged += (o, e) => x(o, e));
eventHandlerSequence.Enqueue(x => fuelTank.FuelFull += x);
Test.ExpectEventSequence(eventHandlerSequence, () => fuelTank.FillFuel());
}
}
Basically I've only changed the order of expected events in test method. If you are still getting incorrect results after changing the loop I think the problem must be outside of your pasted code scope. I'm using VS 2013 community + resharper 8 , nunit 2.6.4.14350
Edit: different approach
I was trying to solve the problem you actually posted, but maybe this will actually be what you want:
wouldn't you consider trying a simplified version of your approach?:
[Test()]
public void FillFuel_Test([Values(1, 5, 10, 100)]float maxFuel)
{
var fuelTank = new Fueled()
{
MaxFuel = maxFuel
};
var expectedEventSequence = new[]
{
"FuelChanged",
"FuelFull"
};
var triggeredEventSequence = new List<string>();
fuelTank.FuelChanged += (o, e) => triggeredEventSequence.Add("FuelChanged");
fuelTank.FuelFull += (o, e) => triggeredEventSequence.Add("FuelFull");
fuelTank.FillFuel();
Assert.AreEqual(expectedEventSequence,triggeredEventSequence);
}
i have an integer whose value changes, i like to know if there is a way to check whether the value is same for 2 sec.
like if num = 2 for 2 sec then Messagebox.show("for 2 sec");
cause my numbers are changing instantly.
How about make it keep track of when it last changed?
int _num;
public int num
{
get { return _num; }
set
{
if(value != _num)
{
_num = value;
numModified = DateTime.Now;
}
}
}
public DateTime numModified { get; private set; }
This might be superfluous, I like using DispatcherTimer. This will tick every 2 seconds to look for a change:
private DispatcherTimer _checkNumberTimer = null;
private int _myNumber = int.MinValue;
private int _lastValue = int.MaxValue;
public Constructor1(){
_checkNumberTimer = new DispatcherTimer();
_checkNumberTimer.Tick += new System.EventHandler(HandleCheckNumberTick);
_checkNumberTimer.Interval = new TimeSpan(0, 0, 0, 2); //Timespan of 2 seconds
_checkNumberTimer.Start();
}
private void HandleLoginOrderDispatcherTick(object sender, System.EventArgs e) {
if(_myNumber == _lastValue){
MessageBox.Show("Alert!");
_checkNumberTimer.Stop(); //If you want
}
_lastValue = _myNumber;
}
private void SomeOtherCodeAffectingMyNumber(int something){
_myNumber = something;
}
You would need to include System.Windows.Threading.
The good thing about DispatcherTimer is that it handles all the work of delegating to the UI thread.
Consider implementing INotifyPropetyChanged interface from System.ComponentModel - here is the description with example.
Try to create a property, a Bool, buffer and a Timer. Now change the setter like this:
public int Variable
{
get { return _variable; }
set
{
yourtimer.Stop();
IsLongerThanTwoSec = false;
_variable = value;
yourtimer.Start();
}
}
Create a timer like this and set this .Elapsed:
bool IsLongerThanTwoSec = false;
Timer timer = new Timer(2000);
timer.Elapsed += (e, s) => IsLongerThanTwoSec = true;
if IsLongerThanTwoSec is false it is not; otherwise, it is.