Strongly typed integers

As a thought experiment on a hobby project, I was thinking about how to avoid this kind of subtle mistakes / typos:

public void MyMethod(int useCaseId)
{
    // Do something with the useCaseId
}

public void SomeOtherMethod()
{
    int userId = 12;
    int useCaseId = 15;
    MyMethod(userId); // Ooops! Used the wrong value!
}

This error will be difficult to find, because theres no compile-time error, and you won’t even get an exception at runtime. You just get "unexpected results."

To solve this in a simple way, Ive experimented with empty enumeration definitions. It is effective to make the user identifier a data type (without even reaching the class or structure):

public enum UseCaseId { // Empty… }

public enum UserId { // Empty… }

public void MyMethod(UseCaseId useCaseId)
{
   // Do something with the useCaseId
}

public void SomeOtherMethod()
{
   UserId userId = (UserId)12;
   UseCaseId useCaseId = (UseCaseId)15;
   MyMethod(userId); // Compile error!!
}

What do you think?

+5
source share
6 answers

UserId UseCaseId, int, :

public class UserId
{
    public int Id { get; private set; }

    public UserId(int id)
    {
       id_ = id;
    }

    public static implicit operator UserId(int id)
    {
        return new UserId(id);
    }

    public static void Main()
    {
        UserId id1 = new UserId(1);
        UserId id2 = 2;
    }
}

, , .

+6

Haskell, , :

data UserId    = UserId    Int
data UseCaseId = UseCaseId Int

, UserId Int, UserId , :

doSomething (UserId 12) (UseCaseId 15)

Niall C. Int. , 10 .

+2

- , :

 public class Id<T> {
    private readonly int _Id;

    private Id(int id) {
        _Id = id;
    }

    public static implicit operator int(Id<T> id) {
        return id._Id;
    }

    public static implicit operator Id<T>(int id) {
        return new Id<T>(id);
    }
}

public void MyMethod(Id<UseCase> useCaseId)
{
   // Do something with the useCaseId
}

public void SomeOtherMethod()
{
   Id<User> userId = 12;
   Id<UseCase> useCaseId = 15;
   MyMethod(userId); // Compile error!!
}

, Id , , . , , .

+2

, .
, .

+1

, FWIW... codeplex " " , Angle, Azimuth, Distance, Latitude, Longitude, Radian .. //, "". , , , , . , , , .

, , , , ( ) ( ):

Angle a = new Angle(45); //45 degrees
SomeObject o = new SomeObject();
o.Rotate(a); //Ensure that only Angle can be sent to Rotate

Angle a = (Angle)45.0;

Radians r = Math.PI/2.0;
Angle a = (Angle)r;

, , "" . ( ). ( "" ) , API , "".

+1

MyMethod .

public void MyMethod(int useCaseId)
{
    if(!IsValidUseCaseId(useCaseId))
    {
         throw new ArgumentException("Invalid useCaseId.");
    }
    // Do something with the useCaseId
}

public bool IsValidUseCaseId(int useCaseId)
{
    //perform validation here, and return True or False based on your condition.
}

public void SomeOtherMethod()
{
    int userId = 12;
    int useCaseId = 15;
    MyMethod(userId); // Ooops! Used the wrong value!
}
0
source

All Articles