Ruby-like instance variable syntax in C-Sharp

From eqqon

(Difference between revisions)
Jump to: navigation, search
Henon (Talk | contribs)
(New page: == 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 unre...)
Newer edit →

Revision as of 21:09, 30 October 2007

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;
    }

}