Subscribe to The Madman Scribblings        RSS Feed
-----

C# Without the Noise

Icon 1 Comments
C# Without the Noise

I've been toying around with the idea on removing (what I perceive) as clutter and noise from C#.
Happened looking at the Money Type for the CLR project and thought it would be good example to see what affect it had.

Original
using System;

namespace System
{
    public struct Money : IEquatable<Money>,
                          IComparable<Money>,
                          IFormattable,
                          IConvertible
    {
        public static implicit operator Money(Byte value)
        { return new Money((Decimal)value);  }
        public static implicit operator Money(SByte value)
        { return new Money((Decimal)value);  }
        public static implicit operator Money(Single value)
        { return new Money((Decimal)value);  }
        public static implicit operator Money(Double value)
        { return new Money((Decimal)value);  }

        public static implicit operator Money(Decimal value)
        { return new Money(value); }

        public static implicit operator Decimal(Money value)
        {
            return value.computeValue();
        }

        public static implicit operator Money(Int16 value)
        {
            return new Money((Decimal)value);
        }

        public static implicit operator Money(Int32 value)
        {
            return new Money((Decimal)value);
        }

        public static implicit operator Money(Int64 value)
        {
            return new Money((Decimal)value);
        }

        public static implicit operator Money(UInt16 value)
        {
            return new Money((Decimal)value);
        }

        public static implicit operator Money(UInt32 value)
        {
            return new Money((Decimal)value);
        }

        public static implicit operator Money(UInt64 value)
        {
            return new Money((Decimal)value);
        }

        public static Money operator -(Money value)
        {
            return new Money(-value._units, -value._decimalFraction, value._currency);
        }

        public static Money operator +(Money left, Money right)
        {
            if (left.Currency != right.Currency) throw differentCurrencies();

            Int32 fractionSum = left._decimalFraction + right._decimalFraction;

            Int64 overflow = 0;
            Int32 fractionSign = Math.Sign(fractionSum);
            Int32 absFractionSum = Math.Abs(fractionSum);

            if (absFractionSum >= FractionScale)
            {   overflow = fractionSign;
                absFractionSum -= (Int32)FractionScale;
                fractionSum = fractionSign * absFractionSum;
            }

            Int64 newUnits = left._units + right._units + overflow;

            if (fractionSign < 0 && Math.Sign(newUnits) > 0)
            {
                newUnits -= 1;
                fractionSum = (Int32)FractionScale - absFractionSum;
            }

            return new Money(newUnits, fractionSum, left.Currency);
        }

        public static Money operator -(Money left, Money right)
        {
            if (left.Currency != right.Currency) throw differentCurrencies();
            return left + -right;
        }

        public static Money operator *(Money left, Decimal right)
        {
            return ((Decimal)left * right);
        }

        public static Money operator /(Money left, Decimal right)
        {
            return ((Decimal)left / right);
        }

        public static Boolean operator ==(Money left, Money right)
        {
            return left.Equals(right);
        }

        public static Boolean operator !=(Money left, Money right)
        {
            return !left.Equals(right);
        }

        public static Boolean operator >(Money left, Money right)
        {
            return left.CompareTo(right) > 0;
        }

        public static Boolean operator <(Money left, Money right)
        {
            return left.CompareTo(right) < 0;
        }

        public static Boolean operator >=(Money left, Money right)
        {
            return left.CompareTo(right) >= 0;
        }

        public static Boolean operator <=(Money left, Money right)
        {
            return left.CompareTo(right) <= 0;
        }

        private const Decimal FractionScale = 1E9M;
        private readonly Currency _currency;
        private readonly Int64 _units;
        private readonly Int32 _decimalFraction;

        public Money(Decimal value)
        {
            checkValue(value);

            _units = (Int64)value;
            _decimalFraction = (Int32)Decimal.Round((value - _units) * FractionScale);

            if (_decimalFraction >= FractionScale)
            {
                _units += 1;
                _decimalFraction = _decimalFraction - (Int32)FractionScale;
            }

            _currency = Currency.FromCurrentCulture();
        }

        public Money(Decimal value, Currency currency) : this(value)
        {
            _currency = currency;
        }

        private Money(Int64 units, Int32 fraction, Currency currency)
        {
            _units = units;
            _decimalFraction = fraction;
            _currency = currency;
        }

        public override Int32 GetHashCode()
        {
            return 207501131 ^ _units.GetHashCode() ^ _currency.GetHashCode();
        }

        public override Boolean Equals(Object obj)
        {
            if (!(obj is Money))
            {
                return false;
            }

            Money other = (Money)obj;
            return Equals(other);
        }

        public override String ToString()
        {
            return computeValue().ToString("C");
        }

        public String ToString(String format)
        {
            return computeValue().ToString(format);
        }

        public Currency Currency
        {
            get { return _currency; }
        }

        #region Implementation of IEquatable<Money>

        public Boolean Equals(Money other)
        {
            checkCurrencies(other);

            return _units == other._units &&
                   _decimalFraction == other._decimalFraction;
        }

        #endregion

        #region Implementation of IComparable<Money>

        public Int32 CompareTo(Money other)
        {
            checkCurrencies(other);

            Int32 unitCompare = _units.CompareTo(other._units);

            return unitCompare == 0
                       ? _decimalFraction.CompareTo(other._decimalFraction)
                       : unitCompare;
        }

        #endregion

        #region Implementation of IFormattable

        public String ToString(String format, IFormatProvider formatProvider)
        {
            return computeValue().ToString(format, formatProvider);
        }

        #endregion

        #region Implementation of IConvertible

        public TypeCode GetTypeCode()
        {
            return TypeCode.Object;
        }

        public Boolean ToBoolean(IFormatProvider provider)
        {
            return _units == 0 && _decimalFraction == 0;
        }

        public Char ToChar(IFormatProvider provider)
        {
            throw new NotSupportedException();
        }

        public SByte ToSByte(IFormatProvider provider)
        {
            return (SByte)computeValue();
        }

        public Byte ToByte(IFormatProvider provider)
        {
            return (Byte)computeValue();
        }

        public Int16 ToInt16(IFormatProvider provider)
        {
            return (Int16)computeValue();
        }

        public UInt16 ToUInt16(IFormatProvider provider)
        {
            return (UInt16)computeValue();
        }

        public Int32 ToInt32(IFormatProvider provider)
        {
            return (Int32)computeValue();
        }

        public UInt32 ToUInt32(IFormatProvider provider)
        {
            return (UInt32)computeValue();
        }

        public Int64 ToInt64(IFormatProvider provider)
        {
            return (Int64)computeValue();
        }

        public UInt64 ToUInt64(IFormatProvider provider)
        {
            return (UInt64)computeValue();
        }

        public Single ToSingle(IFormatProvider provider)
        {
            return (Single)computeValue();
        }

        public Double ToDouble(IFormatProvider provider)
        {
            return (Double)computeValue();
        }

        public Decimal ToDecimal(IFormatProvider provider)
        {
            return computeValue();
        }

        public DateTime ToDateTime(IFormatProvider provider)
        {
            throw new NotSupportedException();
        }

        public String ToString(IFormatProvider provider)
        {
            return ((Decimal)this).ToString(provider);
        }

        public Object ToType(Type conversionType, IFormatProvider provider)
        {
            throw new NotSupportedException();
        }

        #endregion

        private Decimal computeValue()
        {
            return _units + _decimalFraction / FractionScale;
        }

        private static Exception differentCurrencies()
        {
            return new InvalidOperationException("Money values are in different " +
                                                 "currencies. Convert to the same " +
                                                 "currency before performing " +
                                                 "operations on the values.");
        }

        private static void checkValue(Decimal value)
        {
            if (value < Int64.MinValue || value > Int64.MaxValue)
            {
                throw new ArgumentOutOfRangeException("value",
                                                      value,
                                                      "Money value must be between " +
                                                      Int64.MinValue + " and " +
                                                      Int64.MaxValue);
            }
        }

        private void checkCurrencies(Money other)
        {
            if (other.Currency != Currency)
            {
                throw differentCurrencies();
            }
        }
    }
}



The Decluttering Begins

    General Appearence
  • What if C# was line-orientated? So the semi-colon ; on the end of statement is no longer needed.
  • Indentation is equivalent using { } or scoping, modifier and return type are inherited by descendant indentations.
  • Code Folding will available at the start indentation to the end of the indentation.
    Interfaces
  • <-- Interface is been implemented.
  • return types on interface implementation are not needed, because they come from the template, and can't methods can not be differentiated on return type anyway.
    Statically Check Interpolation
  • $@ Type-Safe and statically checked string interpolation, which means what ever is in braces is check to see if it is visible.
    Copy-Cat Typing
  • (Money left right) Type from the previous is used for type of the next parameter. If the parameter is a different type the parameter must be preceeded by a comma before the type.



using System;
namespace System
  public struct Money
    Implements
      IEquatable<Money>y
      IComparable<Money>
      IFormattable
      IConvertible
    private
      static
        Exception differentCurrencies() return new InvalidOperationException("Money values are in different currencies. Convert to the same currency before performing operations on the values.");
        void checkValue(Decimal value)
          if (value < Int64.MinValue || value > Int64.MaxValue)
            throw new ArgumentOutOfRangeException("value", value, $@"Money value must be between ${Int64.MinValue} and ${Int64.MaxValue}")
      Money
        (Decimal value)
          checkValue(value)
          _units = (Int64)value
          _decimalFraction = (Int32)Decimal.Round((value - _units) * FractionScale)
          if (_decimalFraction >= FractionScale)
            _units++
            _decimalFraction = _decimalFraction - (Int32)FractionScale
          _currency = Currency.FromCurrentCulture()
        (Decimal value, Currency currency) : this(value)    _currency = currency
        (Int64 units, Int32 fraction, Currency currency)
          _units = units
          _decimalFraction = fraction
          _currency = currency     
      const Decimal FractionScale = 1E9M
      readonly
        Currency _currency
        Int64 _units
        Int32 _decimalFraction
      Decimal computeValue()   return (_units + _decimalFraction) / FractionScale
      void checkCurrencies(Money other) if (other.Currency != Currency) throw differentCurrencies()
    public
      override
        Int32 GetHashCode()  return 207501131 ^ _units.GetHashCode() ^ _currency.GetHashCode()
        Boolean Equals(Object obj)
          if (!(obj is Money)) return false
          Money other = (Money)obj
          return Equals(other)
        String ToString()      return computeValue().ToString("C")
      String ToString(String format)  return computeValue().ToString(format)
      Currency Currency
        get: return _currency
	  <-- IEquatable<Money>
        Equals(Money other) 
          checkCurrencies(other)
          return (_units == other._units) && (_decimalFraction == other._decimalFraction)
	  <-- IComparable<Money>
        CompareTo(Money other)
          checkCurrencies(other)
          Int32 unitCompare = _units.CompareTo(other._units)
          return unitCompare == 0 ? _decimalFraction.CompareTo(other._decimalFraction) : unitCompare
      <-- IFormattable
        ToString(String format, IFormatProvider formatProvider) return computeValue().ToString(format, formatProvider)
      <-- ICovertable
	    GetTypeCode  ()  return TypeCode.Object
        ToBoolean    (IFormatProvider provider)  return (_units == 0) && (_decimalFraction == 0)
        ToChar       (IFormatProvider provider)  throw  new NotSupportedException()
        ToSByte      (IFormatProvider provider)  return (SByte)  computeValue()
        ToByte       (IFormatProvider provider)  return (Byte)   computeValue()
        ToInt16      (IFormatProvider provider)  return (Int16)  computeValue()
        ToUInt16     (IFormatProvider provider)  return (UInt16) computeValue()
        ToInt32      (IFormatProvider provider)  return (Int32)  computeValue()
        ToUInt32     (IFormatProvider provider)  return (UInt32) computeValue()
        ToInt64      (IFormatProvider provider)  return (Int64)  computeValue()
        ToUInt64     (IFormatProvider provider)  return (UInt64) computeValue()
        ToSingle     (IFormatProvider provider)  return (Single) computeValue()
        ToDouble     (IFormatProvider provider)  return (Double) computeValue()
        ToDecimal    (IFormatProvider provider)  return computeValue()
        ToDateTime   (IFormatProvider provider)  throw  new NotSupportedException()
        ToString     (IFormatProvider provider)  return ((Decimal)this).ToString(provider)
        ToType       (Type conversionType, IFormatProvider provider)  throw new NotSupportedException()
      static
        operator
          implicit
            Decimal(Money value) return value.computeValue()
            Money
              (Byte    value)    return new Money((Decimal)value)
              (SByte   value)    return new Money((Decimal)value)
              (Single  value)    return new Money((Decimal)value)
              (Double  value)    return new Money((Decimal)value)
              (Decimal value)    return new Money(value)
              (Int16   value)    return new Money((Decimal)value)
              (Int32   value)    return new Money((Decimal)value)
              (Int64   value)    return new Money((Decimal)value)
              (UInt16  value)    return new Money((Decimal)value)
              (UInt32  value)    return new Money((Decimal)value)
              (UInt64  value)    return new Money((Decimal)value)
        Money 
          -  (Money value)       return new Money(-value._units, -value._decimalFraction, value._currency)
          +  (Money left  right)
             if (left.Currency != right.Currency) throw differentCurrencies()
               Int32 fractionSum = left._decimalFraction + right._decimalFraction
               Int64 overflow = 0;
               Int32 fractionSign = Math.Sign(fractionSum)
               Int32 absFractionSum = Math.Abs(fractionSum)
               if (absFractionSum >= FractionScale)
                 overflow = fractionSign
                 absFractionSum -= (Int32)FractionScale
                 fractionSum = fractionSign * absFractionSum  
               Int64 newUnits = left._units + right._units + overflow
               if (fractionSign < 0 && Math.Sign(newUnits) > 0)
                 newUnits -= 1
                 fractionSum = (Int32)FractionScale - absFractionSum
             return new Money(newUnits, fractionSum, left.Currency)
          -  (Money left  right)
             if (left.Currency != right.Currency) throw differentCurrencies()
             return left + -right
          *  (Money left, Decimal right) return ((Decimal)left * right)
          /  (Money left, Decimal right) return ((Decimal)left / right)
        Boolean 
          == (Money left right)   return left.Equals(right)
          != (Money left right)   return !left.Equals(right)
          >  (Money left right)   return left.CompareTo(right) > 0
          <  (Money left right)   return left.CompareTo(right) < 0
          >= (Money left right)   return left.CompareTo(right) >= 0   
          <= (Money left right)   return left.CompareTo(right) <= 0



Conclusions
The Good:
In terms of line count it about a third of the size
I like the clean look.
The Bad: Under the current version properties aren't
      String ToString(String format)  return computeValue().ToString(format)
      Currency Currency
        get: return _currency
	  <-- IEquatable<Money>



Possible fix is to introduce a new Properties modifier
      String ToString(String format)  return computeValue().ToString(format)
      Properties
        Currency Currency
          get: return _currency
      <-- IEquatable<Money>


1 Comments On This Entry

Page 1 of 1

Braber01 Icon

16 April 2012 - 04:34 PM
You should check out the Boo.Net Extension it's kind of like this however it's mostly python syntax but I think you'll be happy with it
0
Page 1 of 1