nepdev
nepdev

Reputation: 977

Dynamic ValueOf Type Enforcement

I would like to define a value type which has a finite set of elements, which in turn are elements of a dynamically defined list at runtime (for example from a database).

With this, I would like to be able to enforce type safety (well, sort of) on a dynamic list without having any of the values available at compile time. I could then use the new type in my code for properties, as return values in methods etc.

(Note - it isn't really type-"safety", i.e. compile-time checking I am after, but rather the ability to conceptually identify "this type of value" in methods and properties - for better understandability and readability of the code.)

Pseudocode:

type Currency = ["USD", "EUR", "GBP"];
Currency cur = new Currency();
cur = "CAD";     // must throw an exception

Currency.Add("CAD");
cur = "CAD";     // now it would NOT throw an exception

It is a bit like a subclass of "String" but String is a sealed class and cannot be derived from.

You could also see it as a type defined as the return of a "ValueOf" function from a list. But this does not give me a way to define a type in C#.

Is there any reasonably sensible way to do what I want?

Upvotes: 3

Views: 64

Answers (1)

StriplingWarrior
StriplingWarrior

Reputation: 156634

Be aware that when you talk about Type Safety, most C# developers will automatically assume you're talking about compile-time checking. Since this isn't what you're asking, some people are getting put off.

What you're asking to do isn't really a language function, but it's something you can do with simple programming concepts:

void Main()
{
    Currency.Add("USD", "EUR", "GBP");
    Currency cur = new Currency("CAD"); // must throw an exception

    Currency.Add("CAD");
    cur = new Currency("CAD"); // now it would NOT throw an exception
}

public class Currency{
    private static List<string> availableValues = new List<string>();

    public static void Add(params string[] values){
    foreach (var value in values)
        {
            availableValues.Add(value);
        }
    }

    public Currency(string value){
        if(!availableValues.Contains(value)){
            throw new ArgumentOutOfRangeException(nameof(value), value + " is not a valid Currency");
        }
        this.Value = value;
    }
    public string Value{get;}

    public override string ToString() => Value;
}

This code assumes you are going to register all the available currencies on startup. If you expect new values to be added while the class is actively being used, you'll want to use something like a ConcurrentDictionary<> instead of a List<>.

And it is possible to create an implicit conversion operator so that the Currency cur = "CAD"; syntax works. You can also create another conversion operator so that a Currency gets converted to a String automatically (rather than needing to access the Value property).

    public static implicit operator Currency(string name) =>
        new Currency(name);

    public static implicit operator string(Currency currency) =>
        currency.Value;

Upvotes: 4

Related Questions