4

I want to create a nested structure where every class represents a country, inheriting the same parent class Country. Each child class should have an enum representing the different states States.

The goal is being able to select a country, then one of its states.

The Content will be saved into a dictionary Dictionary<Tuple<string, Type>, object> where the Types would be Country and Country.States.

I tried making an interface/abstract class with an enum called States to be implemented, but this does not work, as it is a type definition.

Is there any workaround?

public abstract class Country
{
    public abstract enum States { get; }
}

public class CountryA : Country
{
    public new enum States
    {
        StateA,
        StateB,
        StateC,
    }
}
David Walser
  • 183
  • 20
  • I love the smell of X Y in the morning. seriously though you cant. The best you could hope for is accessing the various levels of enums via int, and or some sort of reflection with some sort of adhock implementation – TheGeneral Apr 03 '18 at 11:01
  • 6
    Using enums to store the states of a country sounds like a terrible idea. – crazyGamer Apr 03 '18 at 11:05
  • @crazyGamer Using classes to represent countries is even worse. OP: are you really going to have over 200 classes with thousands of enums? Will you rebuild your application each time a country changes? And their states? – Camilo Terevinto Apr 03 '18 at 11:07
  • 1
    I wonder if `public class Country { public string CountryName;}` and `public class State : Country { public string StateName; }` could fit better. But I dont like it. Anyway having multiple classes for each country sounds hard to mantain. – Cleptus Apr 03 '18 at 11:12
  • guys this is just a university project. I just want to store some examples :D (project is about plugins btw) – David Walser Apr 03 '18 at 11:50

5 Answers5

3

Your design is flawed, you need to create a single Country class with a property e.g. public string[] States { get; set; }.

Then create instances (objects) of your Country class, each with States set to the items that are needed:

var usa    = new Country { Name = "USA",    States = new[] { "Alabama", ... } };
var canada = new Country { Name = "Canada", States = new[] { ... } };
// etc
Peter B
  • 22,460
  • 5
  • 32
  • 69
  • Those instances can be static public members of the class itself and then it's [nearly as enum](https://stackoverflow.com/q/2119714/1997232). – Sinatr Apr 03 '18 at 11:18
  • Instead `string[]` I would suggest a `HashSet` – Cleptus Apr 03 '18 at 11:25
1

You have a few options:

You can create an enum at runtime (see here: Dynamically create an enum), but I don't think that'll suit your needs, as I imagine you're going down the enum route for ease of use in coding than anything else.

You could implement a typesafe enum pattern (see here: typesafe enum pattern), but that's even more coding just for the ability to use a design that mimics enums while your coding the rest of your logic.

My advice is to use a dictionary and build your 'states' at instantiation from a settings file or external data source. After all, countries and their states/cities/etc do change names from time to time. Locking yourself into a hard-coded situation like what you're aiming for isn't going to support such future changes.

Good luck!

[Edited following response from camilo-terevinto]

Danielle Summers
  • 366
  • 2
  • 11
  • "What you're aiming to do cannot be done" Please demonstrate how this is true. Of course it's possible, it'd just take a very long amount of time and it's not even close to worth it – Camilo Terevinto Apr 03 '18 at 11:08
  • @CamiloTerevinto, sure, there's the typesafe enum pattern that could be abused to suit the OP's needs. I'd forgotten about that. – Danielle Summers Apr 03 '18 at 11:17
1

While I certainly agree that your design is most likely flawed, since you'd need hundreds of classes and enums, I disagree entirely with the other answers that "it is not possible".

It's certainly possible using generics (while keeping in mind you cannot restrict entirely to Enums):

public abstract class Country<TStates> 
    where TStates: struct, IConvertible, IFormattable, IComparable
{
    public abstract TStates[] States { get; }
}

public enum UnitedStatesStates
{
    WhoCares, WhoCares2
}

public class UnitedStatesCountry : Country<UnitedStatesStates>
{
    public override UnitedStatesStates[] States { get; }
}

Now, I highly doubt this will be useful in the (not-so-long) term.

Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
  • You can restrict it more with some IL, however im not sure his use cases matches a generics implementation. anyway ill give you an upvote as points are hard won tonight – TheGeneral Apr 03 '18 at 11:44
1

You are asking to make enum inheritable, this is possible to achieve if you don't use enum, but a class with static public members (which can be inherited and have different set of members per type). It behave nearly as enum:

public class Country1
{
    public static State State1 { get; } = new State("State 1");
    public static State State2 { get; } = new State("State 2");
    ...
}

It should be clear what Country1.State1 is, right? The State can be a more complex object than just a string. It doesn't require inheritance as you can see, because country define states as different members.

You can follow same principle to implement long chain of objects: Planet.Continent.Country.State.Province.Town.Street.Hause..


You say

Content will be saved into a dictionary Dictionary<Tuple<string, Type>, object> where the Types would be Country and Country.States.

Don't. Those are different types, that's a poor choice of a key. If you need to enumerate (to find) states, then just add another member to a Country:

public static IEnumerable<State> States
{
    get
    {
        yield return State1;
        yield return State2;
        ...
    }
}

Then the searching for something can be a simple linq:

var stateAInCountry1 = ...Countries.OfType<Contry1>().Single().States.Single(o => o.Name == "A");
var countriesWithStateA = ...Countries.Where(o => o.States.Any(o => o.Name == "A"));

Not sure what problem are you solving by introducing a dictionary, but you can initialize additional data structure with proper key if you provided a way to iterate with easy.

Sinatr
  • 20,892
  • 15
  • 90
  • 319
0

It is not so clear to me, if there is anything else you want to achieve, besides being reminded by the compiler to define these different (!) enums.

Actually they have nothing in common to begin with, so neither the compiler nor you can draw any advantage of that contract.

What you could do is declare it as

public abstract string[] States {get;}

and obtain these strings from the individual enums you define in the derived classes. Then the common thing would probably be that you want the string result for informative purposes or something.

oliver
  • 2,771
  • 15
  • 32