I have three questions:
What do you generally think about my approach to solve the given problem?
What do you think I could further improve performance wise?
The most important one: How do I make my implementation really thread safe?
At first the simplified scenario I'm in:
I am communicating via a messaging system with different devices. I am receiving and sending thousands and thousands of messages in a rather short time period. I am inside of a multithreading environment so a lot of different tasks are sending and expecting messages. For the message reception an event driven approach got us a lot of trouble in the sense of making it thread safe.
I have a few Receiver tasks which get messages from outside and have to deliver these messages to a lot of consumer tasks.
So I came up with a different approach:
Why not have a history of a few thousand messages where every new message is enqueued and the consumer tasks can search backwards from the newest item to the last processed item in order to get all newly arrived messages. Of course this has to be fast and thread safe.
I came up with the idea of a linked ring buffer and implemented the following:
public class LinkedRingBuffer<T>
{
private LinkedRingBufferNode<T> firstNode;
private LinkedRingBufferNode<T> lastNode;
public LinkedRingBuffer(int capacity)
{
Capacity = capacity;
Count = 0;
}
/// <summary>
/// Maximum count of items inside the buffer
/// </summary>
public int Capacity { get; }
/// <summary>
/// Actual count of items inside the buffer
/// </summary>
public int Count { get; private set; }
/// <summary>
/// Get value of the oldest buffer entry
/// </summary>
/// <returns></returns>
public T GetFirst()
{
return firstNode.Item;
}
/// <summary>
/// Get value of the newest buffer entry
/// </summary>
/// <returns></returns>
public T GetLast()
{
return lastNode.Item;
}
/// <summary>
/// Add item at the end of the buffer.
/// If capacity is reached the link to the oldest item is deleted.
/// </summary>
public void Add(T item)
{
/* create node and set to last one */
var node = new LinkedRingBufferNode<T>(lastNode, item);
lastNode = node;
/* if it is the first node, the created is also the first */
if (firstNode == null)
firstNode = node;
/* check for capacity reach */
Count++;
if(Count > Capacity)
{/* deleted all links to the current first so that its eventually gc collected */
Count = Capacity;
firstNode = firstNode.NextNode;
firstNode.PreviousNode = null;
}
}
/// <summary>
/// Iterate through the buffer from the oldest to the newest item
/// </summary>
public IEnumerable<T> LastToFirst()
{
var current = lastNode;
while(current != null)
{
yield return current.Item;
current = current.PreviousNode;
}
}
/// <summary>
/// Iterate through the buffer from the newest to the oldest item
/// </summary>
public IEnumerable<T> FirstToLast()
{
var current = firstNode;
while (current != null)
{
yield return current.Item;
current = current.NextNode;
}
}
/// <summary>
/// Iterate through the buffer from the oldest to given item.
/// If item doesn't exist it iterates until it reaches the newest
/// </summary>
public IEnumerable<T> LastToReference(T item)
{
var current = lastNode;
while (current != null)
{
yield return current.Item;
if (current.Item.Equals(item))
break;
current = current.PreviousNode;
}
}
/// <summary>
/// Iterate through the buffer from the newest to given item.
/// If item doesn't exist it iterates until it reaches the oldest
/// </summary>
public IEnumerable<T> FirstToReference(T item)
{
var current = firstNode;
while (current != null)
{
yield return current.Item;
if (current.Item.Equals(item))
break;
current = current.PreviousNode;
}
}
/// <summary>
/// Represents a linked node inside the buffer and holds the data
/// </summary>
private class LinkedRingBufferNode<A>
{
public LinkedRingBufferNode(LinkedRingBufferNode<A> previousNode, A item)
{
Item = item;
NextNode = null;
PreviousNode = previousNode;
if(previousNode != null)
previousNode.NextNode = this;
}
internal A Item { get; }
internal LinkedRingBufferNode<A> PreviousNode { get; set; }
internal LinkedRingBufferNode<A> NextNode { get; private set; }
}
}
But unfortunately I'm kind of new to the multithreading environment, so how would I make this buffer thread safe for multiple reads and writes?
Thanks!
I think the simplest way would be to have a synchronization object which you would lock on, whenever performing thread-critical code. The code within a lock block is called the critical section, and can only be accessed by one thread at a time. Any other thread wishing to access it will wait, until the lock is released.
Definition and initialization:
private object Synchro;
public LinkedRingBuffer(int capacity)
{
Synchro = new object();
// Other constructor code
}
Usage:
public T GetFirst()
{
lock(Synchro)
{
return firstNode.Item;
}
}
When writing thread-safe code, locking some parts may seem obvious. But if you're not sure whether or not to lock a statement or block of code, for both read and write safety you need to consider:
Whether or not this code can influence the behavior or result of any other locked critical sections.
Whether or not any other locked critical sections can influence this code's behavior or result.
You will also need to rewrite some of your auto-implemented properties to have a backing field. It should be pretty straightforward, however...
Your usage of yield return, while being pretty smart and efficient in a single-thread context, will cause trouble in a multi-threaded context. This is because yield return doesn't release a lock statement (and it shouldn't). You will have to perform materialization in a wrapper, wherever you use yield return.
Your thread-safe code looks like this:
public class LinkedRingBuffer<T>
{
private LinkedRingBufferNode<T> firstNode;
private LinkedRingBufferNode<T> lastNode;
private object Synchro;
public LinkedRingBuffer(int capacity)
{
Synchro = new object();
Capacity = capacity;
Count = 0;
}
/// <summary>
/// Maximum count of items inside the buffer
/// </summary>
public int Capacity { get; }
/// <summary>
/// Actual count of items inside the buffer
/// </summary>
public int Count
{
get
{
lock (Synchro)
{
return _count;
}
}
private set
{
_count = value;
}
}
private int _count;
/// <summary>
/// Get value of the oldest buffer entry
/// </summary>
/// <returns></returns>
public T GetFirst()
{
lock (Synchro)
{
return firstNode.Item;
}
}
/// <summary>
/// Get value of the newest buffer entry
/// </summary>
/// <returns></returns>
public T GetLast()
{
lock (Synchro)
{
return lastNode.Item;
}
}
/// <summary>
/// Add item at the end of the buffer.
/// If capacity is reached the link to the oldest item is deleted.
/// </summary>
public void Add(T item)
{
lock (Synchro)
{
/* create node and set to last one */
var node = new LinkedRingBufferNode<T>(lastNode, item);
lastNode = node;
/* if it is the first node, the created is also the first */
if (firstNode == null)
firstNode = node;
/* check for capacity reach */
Count++;
if (Count > Capacity)
{
/* deleted all links to the current first so that its eventually gc collected */
Count = Capacity;
firstNode = firstNode.NextNode;
firstNode.PreviousNode = null;
}
}
}
/// <summary>
/// Iterate through the buffer from the oldest to the newest item
/// </summary>
public IEnumerable<T> LastToFirst()
{
lock (Synchro)
{
var materialized = LastToFirstInner().ToList();
return materialized;
}
}
private IEnumerable<T> LastToFirstInner()
{
var current = lastNode;
while (current != null)
{
yield return current.Item;
current = current.PreviousNode;
}
}
/// <summary>
/// Iterate through the buffer from the newest to the oldest item
/// </summary>
public IEnumerable<T> FirstToLast()
{
lock (Synchro)
{
var materialized = FirstToLastInner().ToList();
return materialized;
}
}
private IEnumerable<T> FirstToLastInner()
{
var current = firstNode;
while (current != null)
{
yield return current.Item;
current = current.NextNode;
}
}
/// <summary>
/// Iterate through the buffer from the oldest to given item.
/// If item doesn't exist it iterates until it reaches the newest
/// </summary>
public IEnumerable<T> LastToReference(T item)
{
lock (Synchro)
{
var materialized = LastToReferenceInner(item).ToList();
return materialized;
}
}
private IEnumerable<T> LastToReferenceInner(T item)
{
var current = lastNode;
while (current != null)
{
yield return current.Item;
if (current.Item.Equals(item))
break;
current = current.PreviousNode;
}
}
/// <summary>
/// Iterate through the buffer from the newest to given item.
/// If item doesn't exist it iterates until it reaches the oldest
/// </summary>
public IEnumerable<T> FirstToReference(T item)
{
lock (Synchro)
{
var materialized = FirstToReferenceInner(item).ToList();
return materialized;
}
}
private IEnumerable<T> FirstToReferenceInner(T item)
{
var current = firstNode;
while (current != null)
{
yield return current.Item;
if (current.Item.Equals(item))
break;
current = current.PreviousNode;
}
}
/// <summary>
/// Represents a linked node inside the buffer and holds the data
/// </summary>
private class LinkedRingBufferNode<A>
{
public LinkedRingBufferNode(LinkedRingBufferNode<A> previousNode, A item)
{
Item = item;
NextNode = null;
PreviousNode = previousNode;
if (previousNode != null)
previousNode.NextNode = this;
}
internal A Item { get; }
internal LinkedRingBufferNode<A> PreviousNode { get; set; }
internal LinkedRingBufferNode<A> NextNode { get; private set; }
}
}
There can be some optimizations done, for example you don't need to create the LinkedRingBufferNode objects inside the critical section, however you would have to copy the lastNode value to a local variable inside a critical section, before creating the object.
Related
I have a class
public class ProductionQueue
{
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>
/// The title.
/// </value>
public string? Name { get; set; }
/// <summary>
/// Maximum capacity of the queue. Keep it null for inifinte capacity.
/// </summary>
public TimeSpan? Capacity { get; set; }
/// <summary>
/// Gets or sets the tasks.
/// </summary>
/// <value>
/// The tasks.
/// </value>
public IList<Task> Tasks { get; set; } = new List<Task>();
}
Let me just explain that the Task object also contains a variable (probably another Timespan) that contains the time to perform the task.
Is it possible to add an event, an observer or any other behaviour in my ProductionQueue to throw an exception if my Capacity is reached when I add a new task in my Task list?
I have written this simple solution based on your initial code but with using an ObservableCollection, so the capacity can be check every time the collection is modified.
It could be done in more effecient way using the data provided by NotifyCollectionChangedEventArgs (type of modification, NewItems, OldItems etc.) to recompute only what's changed, instead of iterating through the whole collection every time.
// simple object for the sake of this example
public class Task
{
public TimeSpan TimeToPerform;
}
public class ProductionQueue
{
public string? Name { get; set; }
/// <summary>
/// Maximum capacity of the queue. Keep it null for inifinte capacity.
/// </summary>
public TimeSpan? Capacity { get; set;}
public ObservableCollection<Task> Tasks = new ObservableCollection <Task>();
// ProductionQueue ctor
public ProductionQueue(TimeSpan? initialCapacity)
{
this.Capacity = initialCapacity;
// subscribe to know when the collection gets changed
this.Tasks.CollectionChanged += (s, e) => {
Console.WriteLine("collection changed");
this.checkCapacity();
};
}
private void checkCapacity()
{
var totalTime = TimeSpan.Zero;
foreach (var task in this.Tasks)
{
totalTime+= task.TimeToPerform;
}
if (totalTime > this.Capacity)
throw new Exception("queue time capacity exceeded");
}
}
}
And here is an example of program using it:
public static void Main(string[] args)
{
try
{
var PQ = new ProductionQueue(TimeSpan.FromHours(1));
PQ.Tasks.Add(new Task(){ TimeToPerform=TimeSpan.FromMinutes(40)});
PQ.Tasks.Add(new Task(){ TimeToPerform=TimeSpan.FromMinutes(30)});
Console.WriteLine("Tasks added without problem");
}
catch(Exception e)
{
Console.WriteLine("Exception occured: "+e.Message);
}
}
Console output:
collection changed
collection changed
Exception occured: queue time capacity exceeded
in my main WPF application code i need to run .exe app with command prompt. this action is executed inside backgroundworker i have the following code. the code is running readlines.exe app with command prompt and read the output lines into a string (str).
string str;
ProcessStartInfo proc = new ProcessStartInfo();
proc.WindowStyle = ProcessWindowStyle.Hidden;
proc.UseShellExecute = true;
proc.FileName = #"readlines.exe";
proc.Arguments = #"";
proc.UseShellExecute = false;
proc.RedirectStandardOutput = true;
proc.CreateNoWindow = true;
proc.RedirectStandardInput = true;
Process proc1 = Process.Start(proc);
proc1.StandardInput.WriteLine("");
str = proc1.StandardOutput.ReadToEnd();
i want to ad timeout to the below line so when the timeout will be finised the procces will be canceled (as CTR+C) and "str" will get the output text until this point.
str = proc1.StandardOutput.ReadToEnd();
is it possible?
Although the previous answer has already been accepted here is a maybe more useful, secure and performant solution. Further it does not make use of the ReadLine() method which would block until there has been one line written (which may never occur). It uses an instance of StringBuilder and reads from the stream in specifyable data blocks (default size is 128 characters). Furthermore it supports event based notification of read data.
The usage of the class stays the same.
ProcessOutputReader por = new ProcessOutputReader(proc1);
por.StartReading();
// Do whatever you want here
// (e.g. sleep or whatever)
por.StopReading();
// Now you have everything that has been read in por.Data
However I've added the OnDataRead event which is fired every time new data has been read. You can access the data by using e.g. following code:
...
// Subscribe to the event
por.OnDataRead += OnDataReadEventHandler;
...
The callback method / event handler would look something like this:
private void OnDataReadEventHandler(object sender, ProcessOutputReaderEventArgs e)
{
// e.IntermediateDataStore points to the StringBuilder instance which holds
// all the data that has been received until now.
string completeData = e.IntermediateDataStore.ToString();
// e.NewData points to a string which contains the data that has been received
// since the last triggered event (because the event is triggered on each read).
string newData = e.NewData;
}
The modified ProcessOutputReader class looks like this:
/// <summary>
/// Represents the ProcessOutputReader class.
/// </summary>
public class ProcessOutputReader
{
/// <summary>
/// Represents the instance of the thread arguments class.
/// </summary>
private ProcessOutputReaderWorkerThreadArguments threadArguments;
/// <summary>
/// Initializes a new instance of the <see cref="ProcessOutputReader"/> class.
/// </summary>
/// <param name="process">The process which's output shall be read.</param>
/// <exception cref="System.ArgumentOutOfRangeException">Is thrown if the specified process reference is null.</exception>
public ProcessOutputReader(Process process)
{
if (process == null)
{
throw new ArgumentOutOfRangeException("process", "The parameter \"process\" must not be null");
}
this.Process = process;
this.IntermediateDataStore = new StringBuilder();
this.threadArguments = new ProcessOutputReaderWorkerThreadArguments(this.Process, this.IntermediateDataStore);
}
/// <summary>
/// Is fired whenever data has been read from the process output.
/// </summary>
public event EventHandler<ProcessOutputReaderEventArgs> OnDataRead;
/// <summary>
/// Gets or sets the worker thread.
/// </summary>
private Thread ReaderThread
{
get;
set;
}
/// <summary>
/// Gets or sets the intermediate data store.
/// </summary>
private StringBuilder IntermediateDataStore
{
get;
set;
}
/// <summary>
/// Gets the data collected from the process output.
/// </summary>
public string Data
{
get
{
return this.IntermediateDataStore.ToString();
}
}
/// <summary>
/// Gets the process.
/// </summary>
public Process Process
{
get;
private set;
}
/// <summary>
/// Stars reading from the process output.
/// </summary>
public void StartReading()
{
if (this.ReaderThread != null)
{
if (this.ReaderThread.IsAlive)
{
return;
}
}
this.ReaderThread = new Thread(new ParameterizedThreadStart(ReaderWorker));
this.threadArguments.Exit = false;
this.ReaderThread.Start(this.threadArguments);
}
/// <summary>
/// Stops reading from the process output.
/// </summary>
public void StopReading()
{
if (this.ReaderThread != null)
{
if (this.ReaderThread.IsAlive)
{
this.threadArguments.Exit = true;
this.ReaderThread.Join();
}
}
}
/// <summary>
/// Fires the OnDataRead event.
/// </summary>
/// <param name="newData">The new data that has been read.</param>
protected void FireOnDataRead(string newData)
{
if (this.OnDataRead != null)
{
this.OnDataRead(this, new ProcessOutputReaderEventArgs(this.IntermediateDataStore, newData));
}
}
/// <summary>
/// Represents the worker method.
/// </summary>
/// <param name="data">The thread arguments, must be an instance of the <see cref="ProcessOutputReaderWorkerThreadArguments"/> class.</param>
private void ReaderWorker(object data)
{
ProcessOutputReaderWorkerThreadArguments args;
try
{
args = (ProcessOutputReaderWorkerThreadArguments)data;
}
catch
{
return;
}
try
{
char[] readBuffer = new char[args.ReadBufferSize];
while (!args.Exit)
{
if (args.Process == null)
{
return;
}
if (args.Process.HasExited)
{
return;
}
if (args.Process.StandardOutput.EndOfStream)
{
return;
}
int readBytes = this.Process.StandardOutput.Read(readBuffer, 0, readBuffer.Length);
args.IntermediateDataStore.Append(readBuffer, 0, readBytes);
this.FireOnDataRead(new String(readBuffer, 0, readBytes));
}
}
catch (ThreadAbortException)
{
if (!args.Process.HasExited)
{
args.Process.Kill();
}
}
}
}
In addition you need the ProcessOutputReaderWorkerThreadArguments class which looks like this:
/// <summary>
/// Represents the ProcessOutputReaderWorkerThreadArguments class.
/// </summary>
public class ProcessOutputReaderWorkerThreadArguments
{
/// <summary>
/// Represents the read buffer size,
/// </summary>
private int readBufferSize;
/// <summary>
/// Initializes a new instance of the <see cref="ProcessOutputReaderWorkerThreadArguments"/> class.
/// </summary>
/// <param name="process">The process.</param>
/// <param name="intermediateDataStore">The intermediate data store.</param>
public ProcessOutputReaderWorkerThreadArguments(Process process, StringBuilder intermediateDataStore)
{
this.ReadBufferSize = 128;
this.Exit = false;
this.Process = process;
this.IntermediateDataStore = intermediateDataStore;
}
/// <summary>
/// Gets or sets a value indicating whether the thread shall exit or not.
/// </summary>
public bool Exit
{
get;
set;
}
/// <summary>
/// Gets or sets the read buffer size in bytes.
/// </summary>
/// <exception cref="System.ArgumentOutOfRangeException">Is thrown if the specified value is not greather than 0.</exception>
public int ReadBufferSize
{
get
{
return this.readBufferSize;
}
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException("value", "The specified value for \"ReadBufferSize\" must be greater than 0.");
}
this.readBufferSize = value;
}
}
/// <summary>
/// Gets the process.
/// </summary>
public Process Process
{
get;
private set;
}
/// <summary>
/// Gets the intermediate data store.
/// </summary>
public StringBuilder IntermediateDataStore
{
get;
private set;
}
}
And the ProcessOutputReaderEventArgs class which looks like this:
/// <summary>
/// Represents the ProcessOutputReaderEventArgs class.
/// </summary>
public class ProcessOutputReaderEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="ProcessOutputReaderEventArgs"/> class.
/// </summary>
/// <param name="intermediateDataStore">The reference to the intermediate data store.</param>
/// <param name="newData">The new data that has been read.</param>
public ProcessOutputReaderEventArgs(StringBuilder intermediateDataStore, string newData)
{
this.IntermediateDataStore = intermediateDataStore;
this.NewData = newData;
}
/// <summary>
/// Gets the reference to the intermediate data store.
/// </summary>
public StringBuilder IntermediateDataStore
{
get;
private set;
}
/// <summary>
/// Gets the new data that has been read.
/// </summary>
public string NewData
{
get;
private set;
}
}
Some example how to achieve this (attention, code not tested and can be improved)
ProcessOutputReader por = new ProcessOutputReader(proc1);
por.StartReading();
// Do whatever you want here
// (e.g. sleep or whatever)
por.StopReading();
// Now you have everything that has been read in por.Lines
The class would look like:
public class ProcessOutputReader
{
public ProcessOutputReader(Process process)
{
this.Process = process;
this.Lines = new List<string>();
}
public List<string> Lines
{
get;
private set;
}
public Process Process
{
get;
private set;
}
private Thread ReaderThread
{
get;
set;
}
public void StartReading()
{
if (this.ReaderThread == null)
{
this.ReaderThread = new Thread(new ThreadStart(ReaderWorker));
}
if (!this.ReaderThread.IsAlive)
{
this.ReaderThread.Start();
}
}
public void StopReading()
{
if (this.ReaderThread != null)
{
if (this.ReaderThread.IsAlive)
{
this.ReaderThread.Abort();
this.ReaderThread.Join();
}
}
}
private void ReaderWorker()
{
try
{
while (!this.Process.HasExited)
{
string data = this.Process.StandardOutput.ReadLine();
this.Lines.Add(data);
}
}
catch (ThreadAbortException)
{
if (!this.Process.HasExited)
{
this.Process.Kill();
}
}
}
}
I am wondering about a certain functionality in C#...
I would like to have a List<Object> MyList();, which I could .Add(new Object()) finite amount of times. Let's say I added 5 items, and if I would add sixth, then the last item would be destroyed, and this sixth element would be put on top of the list.
Is there any built-in mechanism in c# that does that?
There is no build-in collection in Framework as Servy said. However, you can make a CircularBuffer like this -
namespace DataStructures
{
class Program
{
static void Main(string[] args)
{
var buffer = new CircularBuffer<int>(capacity: 3);
while (true)
{
int value;
var input = Console.ReadLine();
if (int.TryParse(input, out value))
{
buffer.Write(value);
continue;
}
break;
}
Console.WriteLine("Buffer: ");
while (!buffer.IsEmpty)
{
Console.WriteLine(buffer.Read());
}
Console.ReadLine();
}
}
}
namespace DataStructures
{
public class CircularBuffer<T>
{
private T[] _buffer;
private int _start;
private int _end;
public CircularBuffer()
: this(capacity: 3)
{
}
public CircularBuffer(int capacity)
{
_buffer = new T[capacity + 1];
_start = 0;
_end = 0;
}
public void Write(T value)
{
_buffer[_end] = value;
_end = (_end + 1) % _buffer.Length;
if (_end == _start)
{
_start = (_start + 1) % _buffer.Length;
}
}
public T Read()
{
T result = _buffer[_start];
_start = (_start + 1) % _buffer.Length;
return result;
}
public int Capacity
{
get { return _buffer.Length; }
}
public bool IsEmpty
{
get { return _end == _start; }
}
public bool IsFull
{
get { return (_end + 1) % _buffer.Length == _start; }
}
}
}
Above code is from PluralSight - Scott Allen's C# Generics.
None of the built in collections will do this, but you can easily make your own class that has an internal list that has this behavior when adding an item. It's not particularly difficult, but writing out all of the methods that a standard list would use and implementing all of the interfaces List does could be a bit tedious.
In my core library, I have something called a LimitedQueue<T>. This is probably similar to what you're after (you could easily modify it to be a List<T> instead). (Source on GitHub)
using System.Collections.Generic;
namespace Molten.Core
{
/// <summary>
/// Represents a limited set of first-in, first-out objects.
/// </summary>
/// <typeparam name="T">The type of each object to store.</typeparam>
public class LimitedQueue<T> : Queue<T>
{
/// <summary>
/// Stores the local limit instance.
/// </summary>
private int limit = -1;
/// <summary>
/// Sets the limit of this LimitedQueue. If the new limit is greater than the count of items in the queue, the queue will be trimmed.
/// </summary>
public int Limit
{
get
{
return limit;
}
set
{
limit = value;
while (Count > limit)
{
Dequeue();
}
}
}
/// <summary>
/// Initializes a new instance of the LimitedQueue class.
/// </summary>
/// <param name="limit">The maximum number of items to store.</param>
public LimitedQueue(int limit)
: base(limit)
{
this.Limit = limit;
}
/// <summary>
/// Adds a new item to the queue. After adding the item, if the count of items is greater than the limit, the first item in the queue is removed.
/// </summary>
/// <param name="item">The item to add.</param>
public new void Enqueue(T item)
{
while (Count >= limit)
{
Dequeue();
}
base.Enqueue(item);
}
}
}
You would use it like this:
LimitedQueue<int> numbers = new LimitedQueue<int>(5);
numbers.Enqueue(1);
numbers.Enqueue(2);
numbers.Enqueue(3);
numbers.Enqueue(4);
numbers.Enqueue(5);
numbers.Enqueue(6); // This will remove "1" from the list
// Here, "numbers" contains 2, 3, 4, 5, 6 (but not 1).
You can use a Queue with a fixed size. Just call .ToList() afterwards.
Fixed size queue which automatically dequeues old values upon new enques
Today I've discovered that the FW 4.5 has their own undoredo manager (if I understood good) http://msdn.microsoft.com/en-us/library/System.ComponentModel.Design.UndoEngine%28v=vs.110%29.aspx
Well, I can't find any example about how to start using this class just to make a simple undo/redo of a text based control, I know other alternatives to do undoable things, but just I want to learn how to use this.
When I try to use the constructor it has any parameter to be passed, and also the Intellisense does not shows me any method for the System.ComponentModel.Design.UndoEngine class, really I don't know how I could use it.
Someone could illustrate ours with an example for C# or VBNET? (I preffer VBNET documentation if possibly, please)
UndoEngine is an abstract class, Visual Studio and Designers have implemented UndoEngine in their own way, and those must be private or not available for redistribution. You will not be able to use it, in fact abstract class is just an interface with little implementation, it is not at all an undo framework.
You still have to write your own undo management, however benefit of deriving your undo engine from UndoEngine class is, it can be easily hosted/integrated with VS and other MS based editors.
If you want to provide an editing experience inside Visual Studio document editor, then you have to derive your Undo framework class from UndoEngine, VS will automatically highlight disable undo/redo buttons and will call undo/redo methods on your class.
If you want to use UndoEngine inside your own application, UndoEngine will not help you for anything, you will have to write every functionality by yourself. UndoEngine just manages stack of Undo/Redo, real work is inside UndoUnit. It is based on Unit of Work concept, where your every action should actually represent a work that can be undone.
The simplest UndoEngine Implementation
Let us say you are changing a global variable,
// following code uses big UndoUnit
public void SetGlobalVariable(object v){
var oldValue = GlobalVariable;
GlobalVariable = v;
var action = new UndoUnit{
UndoAction = ()=>{
GlobalVariable = oldValue;
},
RedoAction = ()=>{
GlobalVariable = v;
}
};
UndoManager.Add(action);
}
/// <summary>
/// Used to indicates the designer's status
/// </summary>
public enum UndoUnitState
{
Undoing,
Redoing,
}
/// <summary>
/// A UndoUnitBase can be used as a IOleUndoUnit or just a undo step in
/// a transaction
/// </summary>
public class UndoUnitBase : IOleUndoUnit
{
public Action UndoAction {get;set;}
public Action RedoAction {get;set;}
private string name = null;
private Guid clsid = Guid.Empty;
private bool inDoAction = false;
private bool isStillAtTop = true;
private UndoUnitState unitState = UndoUnitState.Undoing;
protected UndoUnit UnitState
{
get { return unitState; }
set { unitState = value; }
}
/// <summary>
/// </summary>
/// <param name="undoManager"></param>
/// <param name="name"></param>
internal UndoUnitBase(string name)
{
this.name = name;
}
~UndoUnitBase()
{
}
/// <summary>
/// </summary>
protected bool InDoAction
{
get
{
return inDoAction;
}
}
public string UndoName
{
get
{
return name;
}
set
{
this.name = value;
}
}
public Guid Clsid
{
get { return clsid; }
set { clsid = value; }
}
/// <summary>
/// This property indicates whether the undo unit is at the top (most recently added to)
/// the undo stack. This is useful to know when deciding whether undo units for operations
/// like typing can be coallesced together.
/// </summary>
public bool IsStillAtTop
{
get { return isStillAtTop; }
}
/// <summary>
/// This function do the actual undo, and then revert the action to be a redo
/// </summary>
/// <returns>objects that should be selected after DoAction</returns>
protected abstract void DoActionInternal();
/// <devdoc>
/// IOleUndoManager's "Do" action.
/// </devdoc>
void IOleUndoUnit.Do(IOleUndoManager oleUndoManager)
{
Do(oleUndoManager);
}
protected virtual int Do(IOleUndoManager oleUndoManager)
{
try
{
if(unitState== UndoUnitState.Undoing){
UndoAction();
}else{
RedoAction();
}
unitState = (unitState == UndoUnitState.Undoing) ? UndoUnitState.Redoing : UndoUnitState.Undoing;
if (oleUndoManager != null)
oleUndoManager.Add(this);
return VSConstants.S_OK;
}
catch (COMException e)
{
return e.ErrorCode;
}
catch
{
return VSConstants.E_ABORT;
}
finally
{
}
}
/// <summary>
/// </summary>
/// <returns></returns>
void IOleUndoUnit.GetDescription(out string pBstr)
{
pBstr = name;
}
/// <summary>
/// </summary>
/// <param name="clsid"></param>
/// <param name="pID"></param>
/// <returns></returns>
void IOleUndoUnit.GetUnitType(out Guid pClsid, out int plID)
{
pClsid = Clsid;
plID = 0;
}
/// <summary>
/// </summary>
void IOleUndoUnit.OnNextAdd()
{
// We are no longer the top most undo unit; another one was added.
isStillAtTop = false;
}
}
public class MyUndoEngine : UndoEngine, IUndoHandler
{
Stack<UndoEngine.UndoUnit> undoStack = new Stack<UndoEngine.UndoUnit>();
Stack<UndoEngine.UndoUnit> redoStack = new Stack<UndoEngine.UndoUnit>();
public ReportDesignerUndoEngine(IServiceProvider provider) : base(provider)
{
}
#region IUndoHandler
public bool EnableUndo {
get {
return undoStack.Count > 0;
}
}
public bool EnableRedo {
get {
return redoStack.Count > 0;
}
}
public void Undo()
{
if (undoStack.Count > 0) {
UndoEngine.UndoUnit unit = undoStack.Pop();
unit.Undo();
redoStack.Push(unit);
}
}
public void Redo()
{
if (redoStack.Count > 0) {
UndoEngine.UndoUnit unit = redoStack.Pop();
unit.Undo();
undoStack.Push(unit);
}
}
#endregion
protected override void AddUndoUnit(UndoEngine.UndoUnit unit)
{
undoStack.Push(unit);
}
}
If your question is how to use it at runtime, then the answer is in MSDN:
Specifies generic undo/redo functionality at design time.
So I doubt that it is easily usable at runtime.
If you meant an example of custom user control utilizing this class, I can't find any, sorry.
Find an implementation of the UndoEngine and how to use it right here:
https://github.com/icsharpcode/SharpDevelop/search?q=ReportDesignerUndoEngine&ref=cmdform
HTH
.NET 4.0 has a nice utility class called System.Lazy that does lazy object initialization. I would like to use this class for a 3.5 project. One time I saw an implementation somewhere in a stackoverflow answer but I can't find it anymore. Does someone have an alternative implementation of Lazy? It doesn't need all the thread safety features of the framework 4.0 version.
Updated:
Answers contain a non thread safe and a thread safe version.
Here is an implementation that I use.
/// <summary>
/// Provides support for lazy initialization.
/// </summary>
/// <typeparam name="T">Specifies the type of object that is being lazily initialized.</typeparam>
public sealed class Lazy<T>
{
private readonly object padlock = new object();
private readonly Func<T> createValue;
private bool isValueCreated;
private T value;
/// <summary>
/// Gets the lazily initialized value of the current Lazy{T} instance.
/// </summary>
public T Value
{
get
{
if (!isValueCreated)
{
lock (padlock)
{
if (!isValueCreated)
{
value = createValue();
isValueCreated = true;
}
}
}
return value;
}
}
/// <summary>
/// Gets a value that indicates whether a value has been created for this Lazy{T} instance.
/// </summary>
public bool IsValueCreated
{
get
{
lock (padlock)
{
return isValueCreated;
}
}
}
/// <summary>
/// Initializes a new instance of the Lazy{T} class.
/// </summary>
/// <param name="createValue">The delegate that produces the value when it is needed.</param>
public Lazy(Func<T> createValue)
{
if (createValue == null) throw new ArgumentNullException("createValue");
this.createValue = createValue;
}
/// <summary>
/// Creates and returns a string representation of the Lazy{T}.Value.
/// </summary>
/// <returns>The string representation of the Lazy{T}.Value property.</returns>
public override string ToString()
{
return Value.ToString();
}
}
If you don't need thread-safety, it's pretty easy to put one together with a factory method. I use one very similar to the following:
public class Lazy<T>
{
private readonly Func<T> initializer;
private bool isValueCreated;
private T value;
public Lazy(Func<T> initializer)
{
if (initializer == null)
throw new ArgumentNullException("initializer");
this.initializer = initializer;
}
public bool IsValueCreated
{
get { return isValueCreated; }
}
public T Value
{
get
{
if (!isValueCreated)
{
value = initializer();
isValueCreated = true;
}
return value;
}
}
}
A somewhat simplify version of aaron's
public class Lazy<T> where T : new()
{
private T value;
public bool IsValueCreated { get; private set;}
public T Value
{
get
{
if (!IsValueCreated)
{
value = new T();
IsValueCreated = true;
}
return value;
}
}
}
Some funny (but not very usable) stuff can be added: implicit coversion from delegate:
public static implicit operator Lazy<T>(Func<T> initializer)
{
return new Lazy<T>(initializer);
}
And usage
private static Lazy<int> Value = new Func<int>(() => 24 * 22);
C# compiler have some problem with performing this conversion, for example assigning lambda expression does not work, but it is one more thing causes your colleguas to think a bit :)