Ruby-like instance variable syntax in C-Sharp
From eqqon
Ruby-like instance @variables in C#
Although I am very impressed by C#, Ruby is still my favorite programming language because of it's clean design and unreached flexibility. One of the things I like about Ruby is its way to mark instance variables (in Ruby's terminology) or member variables / fields (in C# terminology). They are prefixed with the @-sign. This makes code very easy to read. To get the same effect most C# programmers rely on a kind of Hungarian style notation which means prefixing with m_. Today I discovered that the C# compiler allows the @-sign as prefix for identifiers. This is useful if you need to name an identifier e.g. a variable like a keyword. How often have I been forced to unwillingly name variables which contain a class-object klass or _class when programming in Ruby. So this is really a nice feature of C#, isn't it?
Even better, the @-prefix can also be used to mark member variables in C#. Since I am virulently against the Hungarian notation for variable names (e.g. lpSzFile) this is exactly what I was looking for. Now programming C# feels much more like Ruby for me. There are still many oddities in C# which I need to get used to or even better I need to overcome by using the new extension methods introduced by C# 3.0.
Example: The class Range
Here is the Range class, a built in Ruby feature which I instantly missed the first day I started programming C# based on the Range by Goran Mitrovic. I refactored it a bit and applied the @-notation to demonstrate its clean readability. Those of you who love Ruby will love this style.
using System; using System.Collections; namespace Eqqon { public struct Range : ICollection, IEnumerable { public Range(int startValue, int endValue) { @start = startValue; @end = 0; this.End = endValue; } public Range(ICollection col) { @start = 0; @end = ((col != null) ? col.Count : 0) - 1; } public int Start { get { return @start; } set { @start = value; if (@end < (@start - 1)) @end = @start - 1; } } public int End { get { return @end; } set { @end = (value >= @start) ? value : (@start - 1); } } public int Mid { get { return (@start + @end) / 2; } } public void Set(int start, int end) { @start = start; this.End = end; } public bool Contains(int value) { return (@start <= value) && (value <= @end); } public static bool Contains(int left, int value, int right) { return (left <= value) && (value <= right); } public int Saturate(int value) { return (value > @start) ? ((value < @end) ? value : @end) : @start; } public static int Saturate(int left, int value, int right) { return (value > left) ? ((value < right) ? value : right) : left; } public static Range operator &(Range r1, Range r2) { return new Range(Math.Max(r1.Start, r2.Start), Math.Min(r1.End, r2.End)); } public static Range operator |(Range r1, Range r2) { return new Range(Math.Min(r1.Start, r2.Start), Math.Max(r1.End, r2.End)); } public void Offset(int o) { @start += o; @end += o; } public void Resize(int s) { End += s; } public int Count { get { return @end - @start + 1; } } public bool Empty { get { return @end == (@start - 1); } } public int this[int index] { get { if (Count == 0) throw new InvalidOperationException("Cannot perform this operation on a Range with Count 0!"); return @start + index % Count; } } public void CopyTo(Array array, int index) { if (array == null) throw new ArgumentNullException("Array must not be null!"); if (index < 0) throw new ArgumentOutOfRangeException("index", index, "Index must not be < 0"); if (array.Rank != 1) throw new ArgumentException("Array must not be multidimensional.", "array"); if (!new Range(array).Contains(index)) throw new ArgumentOutOfRangeException("index"); if (Count == 0) return; if (!new Range(array).Contains(index + Count - 1)) throw new ArgumentOutOfRangeException("index"); foreach (int i in this) { array.SetValue(i, index + i - @start); } } public bool IsSynchronized { get { return false; } } public object SyncRoot { get { return this; } } public IEnumerator GetEnumerator() { return new Iterator(ref this); } private class Iterator : IEnumerator { public Iterator(ref Range r) { @range = r; Reset(); } public object Current { get { if (!@range.Contains(@pos + @range.Start)) throw new InvalidOperationException(); return @range[@pos]; } } public bool MoveNext() { return @range.Contains((++@pos) + @range.Start); } public void Reset() { @pos = -1; } private Range @range; private int @pos; }; private int @start; private int @end; } }