views:

894

answers:

4

I have a bunch of code that has lots integers with different meanings (I'd rather a general solution but for a specific example: day-of-the-month vs. month-of-the-year vs. year etc.). I want to be able to overload a class constructor based on these meanings.

For example

int a; // takes role A
int b; // takes role B

var A = new Foo(a);  // should call one constructor
var B = new Foo(b);  // should call another constructor

Now clearly that won't work but if I could define a type (not just an alias) that is an int in all but name like this:

typedef int TypeA;  // stealing the C syntax
typedef int TypeB;

I could do the overloading I need and let the type system keep track of what things are what. In particular this would allow me to be sure that values are not mixed up, for example a value returned from a function as a year is not used as a day-of-the-month.

Is there any way short of class or struct wrappers to do this in c#?


It would be nice if the solution would also work for floats and doubles.

+6  A: 

This may be way off, but couldnt you use an enum for this? Enum base is int, but is typed, and you could define different constructors based on the type of enum passed.

Mike_G
That would work (+1) for cases where only a small range is allowed (month-of-year) but in other cases (year) you would start needing to hack to make c# allow values that are not otherwise defined.
BCS
in c# int.Max isnt exactly small ;-p
Mike_G
IIRC c# restricts how you can set an enum type so that it will only have a defined value. So given "enum E { V = 1 } E e;" e can't take the value 2 without some hacking.
BCS
ya, I dont know how you would do that.A suggestion to your latest edit, would an extension method help with that? Like IsMonth, or IsYear? You could write the extension method for enumeration type.http://msdn.microsoft.com/en-us/library/bb383974.aspx
Mike_G
@BCS: An enum value can take any other value of its base type. So if the enum is based off int, you can assign any int to it even if it only defines names for say 1 and 2. You just cast as in myEnumValue = (MyEnumType)42. That's not hacking.
Jeff Yates
+2  A: 

If it's just for the constructor, couldn't you use something like this:

 int dom = 5;
int doy = 100;

Foo b = Foo.NewFromDayoftheMonth(dom);

Foo c = Foo.NewFromDayoftheYear(doy);

where each method are static and create a new Foo object based on the parameters passed in.

This seems like a solution to me, where there isn't much else of one.

Moose
That's one option I have considered, but I want the compiler to check things for me. See my forthcoming edit
BCS
+13  A: 

There is no direct typedef equivalent, but you can do the following:

using TypeA = int;
using TypeB = int;

However, this just aliases the type rather than creating a new strong type. Therefore, the compiler will still treat them as an int when resolving method calls.

A better solution might be to create simple wrapper classes that wraps int and provides implicit casting, such as:

struct TypeA
{
   public TypeA(int value)
   {
      this.realValue = value;
   }

   private int realValue;
   public static implicit operator int(TypeA value)
   {
     return this.realValue;
   }

   public static implicit operator TypeA(int value)
   {
     return new TypeA(value);
   }
}

However, in most situations, an enum would be more appropriate.

Jeff Yates
this as a struct and without the implicit casting is what I ended up doing. It works well.
BCS
Is there a performance penalty for this or are C# compilers good enough to optimize the wrapper away? I'm missing something equivalent to Haskell's newtype in C# (they're essentially what you just did, but are erased during compilation (but after type-checking)).
FunctorSalad
I don't know whether there would be much of a performance penalty or not. You'd have to try it and see, I'm afraid.
Jeff Yates
A: 

I would also create a struct around the value :

public struct TypeA
{
  private int value;
  public TypeA(int value)
  {
    this.value = value;
  }

  public static explicit operator int(TypeA a)
  {
     return a.value;
  }

  public static explicit operator TypeA(int value)
  {
     return new TypeA(value);
  }
}

you can also declare operators to combine types and methods to provide a rich type around the int value.

A Year type can provde a IsLeap property, a day of month can be restraint to values between 1 and 31 and provide a special arithmetic.

Think Before Coding