I am trying to learn C#. Please tell me if the following code respects coding standards and naming conventions because I want to write beautiful code. The purpose of this program was to help me understand different concepts that I've learned in the last few days but I was having difficulties thoroughly understanding them (especially events and delegates). Please show me what you would change by exemplifying, not just by recommending, because I have a harder time understanding phrases than code.
For example, I've been told to use "async/await and the TPL". I've made a couple simple programs with them, yet I still don’t know how I would apply the concepts here. I'll keep trying while waiting for an answer from you, I'll be reading the chapters from here in the next few hours.
Also, I've been told I should have used Action
. I want to use Action
s because I like Action
and Func
, yet I don’t know where should I use Action
.
TProgram.cs:
#define DEBUG
#define TRACE
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
namespace LearnEventsAndOthers
{
class Program
{
private static void OnTicked(object sender, TickingEventArgs e)
{
Console.WriteLine("Event triggered in object {0} at tick {1}", ((Metronom)sender).Id, e.TickCount);
}
static int Main(string[] args)
{
#region Standard Code - Init
Console.InputEncoding = Console.OutputEncoding = Encoding.UTF8;
Console.SetWindowSize(160, 56);
Console.BufferHeight = 9000;
Console.BufferWidth = 300;
#if DEBUG
Console.WriteLine("The program is running in #DEBUG mode.");
#endif
#if TRACE
Console.WriteLine("The program is running in #TRACE mode.");
#endif
#endregion
// Creating objects and testing overridden functions Equals and GetHashCode...
Metronom m = new Metronom();
Metronom n = new Metronom();
Trace.Listeners.Add(new TextWriterTraceListener(System.IO.File.CreateText("TraceInfo.txt")));
Trace.AutoFlush = true;
Debug.Indent();
Debug.WriteLineIf(!m.Equals(n), string.Format("Objects n and m have been created and they have different IDs: {0} vs {1}", m.GetHashCode(), n.GetHashCode()));
Debug.Assert(!m.Equals(n), "Objects n and m have been created but they are equal! This shouldn't happen.");
// Testing event subscription...
m.TickedEvent += OnTicked;
n.TickedEvent += OnTicked;
// Testing destructors...
Console.WriteLine(Environment.NewLine + "Press any key to destroy the objects!" + Environment.NewLine);
Console.ReadKey();
m.RequestStop();
Console.WriteLine(Environment.NewLine + "The first object was destroyed. Waiting 5 seconds before destroying the second...");
Thread.Sleep(5000);
n.RequestStop();
#region Standard Code - Exit
Console.WriteLine("\nPress <enter> to exit!");
Console.ReadLine();
return 0;
#endregion
}
}
}
Metronom.cs:
#define DEBUG
#define TRACE
using System;
using System.Diagnostics;
using System.Threading;
namespace LearnEventsAndOthers
{
public class TickingEventArgs : EventArgs
{
public long TickCount { get; set; }
}
public class Metronom
{
public event EventHandler<TickingEventArgs> TickedEvent;
public Guid Id { get; private set; }
public int RandomStop { get; private set; } // Used to trigger the TickedEvent at random time spans.
protected internal const int SpeedModifier = 17777777; // Used to control the speed at which events are triggered. Higher means slower.
// Why did I make it protected internal? I really wanted to use the access modifier "internal".
private readonly Thread _t;
private volatile bool _shouldStop;
public Metronom()
{
this.Id = Guid.NewGuid();
checked
{
int hashCode = Math.Abs(this.GetHashCode());
while (hashCode > SpeedModifier)
hashCode -= SpeedModifier;
RandomStop = hashCode;
}
_t = new Thread(Clock) { IsBackground = true };
_t.Start();
Debug.WriteLine("Object initialized with GUID HashCode " + this.GetHashCode());
}
~Metronom()
{
_t.Join();
Debug.WriteLineIf(_t.IsAlive == false, string.Format("Thread from object {0} has been stopped.", this.Id));
Debug.Assert(_t.IsAlive == false, string.Format("Thread from object {0} is still running.", this.Id));
}
protected virtual void OnTickedEvent(long l)
{
EventHandler<TickingEventArgs> handler = TickedEvent;
if (handler != null) handler(this, new TickingEventArgs() { TickCount = l });
}
public void RequestStop()
{
_shouldStop = true;
}
private void Clock()
{
Stopwatch c = new Stopwatch();
c.Start();
Debug.WriteLineIf(_t.IsAlive, string.Format("Thread from object {0} has gone LIVE!", this.Id));
while (!_shouldStop)
{
long l = c.ElapsedTicks;
if ((l % this.RandomStop) == 0)
{
OnTickedEvent(l);
}
}
}
public override bool Equals(object obj)
{
return obj is Metronom && (((Metronom)obj).Id.Equals(Id));
}
public override sealed int GetHashCode()
{
return Id.GetHashCode();
}
}
}