Jon,
Jay, regarding the Strategy Pattern, the problem is that type conversions
must be explicitly casted, so each and every type conversion possibility
I realize that, which is why I suggested the Strategy Pattern. Also to limit
the select case statements in each operator.
I would base the Strategy Pattern on the type of one or both of the
arguments to the operators. Something like:
// IArithmetic represents the strategy on how to add to values.
// I suspect this should really be an abstract base class
// with overridable methods, that offer default implementation,
// such as throwing NotSupportedExceptions or similar
// for when you attempt to Divide two XmlDocument values...
private interface IArithmetic
{
object Add(object value1, object value2);
object Subtract(object value1, object value2);
object Multiply(object value1, object value2);
...
}
// IntegerArithmetic knows how to add two integers
// There would be one of these for each System.TypeCode.
private class IntegerArithmetic : IArithmetic
{
// See:
http://www.yoda.arachsys.com/csharp/singleton.html
// on details on implementing Singletons.
static IArithmetic Instance = new IntegerArithmetic();
public object Add(object value1, object value2)
{
return (int)value1 + Convert.ToInt32(value2);
}
public object Subtract(object value1, object value2)
{
return (int)value1 - Convert.ToInt32(value2);
}
public object Multiply(object value1, object value2)
{
return (int)value1 * Convert.ToInt32(value2);
}
...
}
private class BooleanArithmetic : IArithmetic
...
private class SingleArithmetic : IArithmetic
...
// ArithmeticFactory returns a concrete IArithmetic object for the specified
value.
private class ArithmeticFactory
{
public static IArithmetic Create(object value)
{
// If IArithmetic handled all the type specific stuff this would be
your only "select case" statement!
switch (Convert.GetTypeCode(value))
{
case TypeCode.Boolean:
return BooleanArithmetic.Instance;
break;
case TypeCode.Single:
return SingleArithmetic.Instance;
break;
case TypeCode.Int32:
return IntergerArithmetic.Instance;
break;
...
}
}
}
// Then the operators themselves are super simple!
public static Variant operator +(Variant subjectVariant, object value)
{
return new
Variant(ArithmeticFactory.Create(subjectVariant._value).Add(subjectVariant._
value,
value));
}
public static Variant operator -(Variant subjectVariant, object value)
{
return new
Variant(ArithmeticFactory.Create(subjectVariant._value).Subtract(subjectVari
ant._value,
value));
}
public static Variant operator *(Variant subjectVariant, object value)
{
return new
Variant(ArithmeticFactory.Create(subjectVariant._value).Multiply(subjectVari
ant._value,
value));
}
I would consider having ArithmeticFactory.Create accept both values & decide
which value should be elevated to the other's type. (Integer & Single
arithmetic should be done in Single not Integer) based loosely on the rules
that VB.NET follows & what COM's Variant rules followed...
Any reason that TypeMap is not implemented in terms of Convert.ChangeType
directly?
private object TypeMap(Type type)
{
return Convert.ChangeType(_value, type);
}
Also I would simply implement functions such as ToBoolean in terms of
Convert also:
public bool ToBoolean()
{
if (this._value is Boolean)
return (bool)_value;
else
return (bool)Convert.ToBoolean(_value);
}
public bool ToBoolean(IFormatProvider formatProvider)
{
return (bool)Convert.ToBoolean(_value, formatProvider);
}
I'm not sure if "_value is Boolean" is faster or slower then ".Type is
typeOf(Boolean)" it just looks cleaner to me...
I'm undecided if I would include "_value is Boolean" in ToBoolean() itself,
my initialize implementation would probably simply be:
public bool ToBoolean()
{
return (bool)Convert.ToBoolean(_value);
}
Then not worry about any performance problems, as Variant represents a
loosely typed value anyway... (read it's inefficiency is what makes it
flexibly!)
NOTE: I am implementing the conversions entirely in System.Convert as then I
am entirely consistent with the framework! Also its less code I need to
write!
Hope this helps
Jay
Jon Davis said:
Jay, regarding the Strategy Pattern, the problem is that type conversions
must be explicitly casted, so each and every type conversion possibility
must be explicitly referenced, as in ([totype])([fromtype])_value.
Otherwise, I would just pass ([type])_value, but it won't work that way
because the explicit custom operators for typecasting for several of the
types sees the object as System.Object as opposed to the case of
([totype])([fromtype])_value.
So a generic Strategy Pattern isn't going to achieve anything for me. This
is one of those rare exceptions; if I was doing a bunch of conditionals to
convert a VALUE, rather than TYPE, that would be different.
But please correct me if I'm forgetting something.
Jon
Jon,
Check out VB.NET 2005, as it adds support for the Overloaded Operators to
VB.NET.
http://lab.msdn.microsoft.com/vs2005/
VB.NET 2002 & 2003 do not support Overloaded Operators so your code doesn't
work per se, unless you call op_Addition, you may want to add "operator"
methods similar to what System.Decimal has, such as Decimal.Add in addition
to the overloaded operators. I normally implement overloaded operators in
terms of the "operator" methods.
You do know that you can use System.Convert.ChangeType to convert an object
from one type to another based on a System.Type variable? In fact you may
want your structure to implement System.IConvertible, and make use of the
System.Convert class to convert types for you...
Also I would seriously consider implementing a Strategy Pattern instead
of
all the If statements in each method. (note Convert.ChangeType will
reduce a
number of if statements, the Strategy should element almost all the
rest).
Otherwise a very impressive piece of work!
Hope this helps
Jay
Hi guys!
Just wanted to let you all know that I created a Variant structure, inspired
by the old VB6 days. This is written in C#, but you can build a CLR/.NET
class library assembly and reference it to your liking.
' Here is an example of use:
' start with a string of "1"
Dim da As Variant = new Variant("1")
' add an integer of 2, becomes 3
da = da + 2
' add a float of 1.3, becomes 4.3
da = da + 1.3F
' add a string, becomes "4.3wow"
da = da + "wow"
' write "4.3wow"
Console.WriteLine(da)
I might actually use this puppy.
Anyway, here it is, source code and all, take a look and be sure to
give
it
5 stars if you find it useful.
http://www.planet-source-code.com/vb/scripts/showcode.asp?txtCodeId=2854&lngWId=10