Home > Software engineering >  How to have type-safety dependent on a number? (like generics are dependent on a Type)
How to have type-safety dependent on a number? (like generics are dependent on a Type)

Time:12-06

I want a Type that is "for" a certain number, and another Type for another number. But I don't want to have to manually define a Type for each number like Level1024 and Level1000. I want it to be simple to instantiate an instance of the Level class for each number, like we can do with generics where we can create a Level<string> and a Level<int> without needing to define a separate Level for each of them.

Here's the idea:

Level<1024> topPlayerOf1K;
Level<1000> Abe = new Level<1000>();
topPlayerOf1K = Abe; //This should show a squiggly line in Visual Studio.

How can I achieve that or something like that?

CodePudding user response:

Numbers literals are not considered types in C# like they are in TypeScript, and cannot be used as generic parameters like template parameters in C .

At the minimum you would have to create types for each of the number literals you want to use. The approach could look like this:

interface IConstantInt { int Value { get; } }

class ConstantInt1000 : IConstantInt { public int Value => 1000; }
class ConstantInt1024 : IConstantInt { public int Value => 1024; }

class Level<TConstantInt> where TConstantInt : IConstantInt { }

var level1000 = new Level<ConstantInt1000>();
var level1024 = new Level<ConstantInt1024>();

It would be good to autogenerate this code if you're going to have many of those. This is not a great solution, but without knowing more about your program and what kind of errors you're trying to prevent, in the abstract, that's a way that you could encode number literals in the type system.

Edit: here's one way that you could access the value inside the class:

class Level<TConstantInt> where TConstantInt : IConstantInt 
{
    readonly int _intValue;

    public Level(TConstantInt constantInt) => 
        _intValue = constantInt.Value;
}

class Level
{
    // Optional utility factory method, helps with type inference
    public static Level<TConstantInt> Create<TConstantInt>(TConstantInt constantInt) 
        where TConstantInt : IConstantInt =>
        new(constantInt);
}

// Now creation looks like this:

var level1000 = Level.Create(new ConstantInt1000());
var level1024 = Level.Create(new ConstantInt1024());
  • Related