Reputation: 209
I want to declare several constant objects that each have two subobjects, and I'd like to store these in an enum
for organizational purposes.
Is it possible to do something like this in C#?
enum Car
{
carA = { 'ford', 'red' }
carB = { 'bmw', 'black' }
carC = { 'toyota', 'white' }
}
Upvotes: 5
Views: 258
Reputation: 77546
You can use a static class to house extension methods that surface your extra data. For example:
enum Car
{
CarA, CarB, CarC
}
public static class Cars
{
public static string[] GetDetails(this Car car)
{
switch (car)
{
case CarA: return new[] { "ford", "red" };
case CarB: return new[] { "bmw", "black" };
case CarC: return new[] { "toyota", "white" };
}
}
}
That being said, it doesn't make much sense to me to return a string array for this. I'd instead declare two extension methods, one for the make, and one for the color:
public static class Cars
{
public static string GetMake(this Car car)
{
switch (car)
{
case CarA: return "ford";
case CarB: return "bmw";
case CarC: return "toyota";
}
}
public static string GetColor(this Car car)
{
switch (car)
{
case CarA: return "red";
case CarB: return "black";
case CarC: return "white";
}
}
}
Then you can use it like so:
Car car = Car.CarA;
string make = car.GetMake();
string color = car.GetColor();
Upvotes: 1
Reputation: 670
What about the usage of attributes?
enum Cars{
[Make("A Make"), Color("A Color")]
CarA,
[Make("B Make"), Color("B Color")]
CarB
}
and then define the attributes like this.
public class MakeAttribute : Attribute
{
public readonly Make make;
public MakeAttribute (Make make)
{
this.make = make;
}
}
Add an extension to the Car type to get the make attribute
public static string GetMake(this Car car)
{
var makeAttr = (MakeAttribute[])car.GetType().GetField(car.ToString()).GetCustomAttributes(typeof(MakeAttribute), false))[0];
return makeAttr.make;
}
And to invoke this getter,
Cars.CarA.GetMake()
Upvotes: 1
Reputation: 42246
No, this is not possible. You can define a static class
public static class Car
{
public static readonly ReadOnlyCollection<string> carA = Array.AsReadOnly(new[]{"ford","red"});
public static readonly ReadOnlyCollection<string> carB = Array.AsReadOnly(new[]{"bmw","black"});
public static readonly ReadOnlyCollection<string> carC = Array.AsReadOnly(new[]{"toyota","white"});
}
I used ReadOnlyCollection<string>
instead of a string[]
in order to preserve the immutability property of enums.
This doesn't satisfy the condition that each of Car
's properties are instances of Car
. You could go further to get what you want using a custom enumeration class, with a private constructor and static instances. Jimmy Bogard has an example implementation and base class available at http://lostechies.com/jimmybogard/2008/08/12/enumeration-classes/ . He provides an extensible base class, which you should look into if you will do this a lot. However, just so you get the idea, a simple implementation that uses this approach with your data would look like:
public sealed class Car : IEquatable<Car> {
// declare and define each of your constants
public static readonly Car carA = new Car("ford", "red");
public static readonly Car carB = new Car("bmw", "black");
public static readonly Car carC = new Car("toyota", "white");
// define an instance-scoped value object to hold your subObjects
private readonly Tuple<string,string> subObjects;
// a private constructor ensures that all your instances will be constant
// and will be defined from within Car
private Car(string make, string color){
// require valid sub objects
if(string.IsNullOrEmpty(make))throw new ArgumentException("Invalid Make","make");
if(string.IsNullOrEmpty(color))throw new ArgumentException("Invalid Color","color");
// create a subObjects tuple to hold your values to simplify value comparison
this.subObjects = Tuple.Create(make,color);
}
// declare public accessors for your
public string Make { get { return this.subObjects.Item1; } }
public string Color { get { return this.subObjects.Item2; } }
// override Equality for value equality, and resulting consistency across AppDomains
public override bool Equals(object obj){ return obj is Car && this.Equals((Car)obj); }
public bool Equals(Car otherCar){ return otherCar != null && this.subObjects.Equals(otherCar.subObjects); }
public override int GetHashCode(){ return this.subObjects.GetHashCode(); }
public static bool operator ==(Car left, Car right){ return left == null ? right == null : left.Equals(right); }
public static bool operator !=(Car left, Car right){ return !(left == right); }
// override ToString() to provide view of values
public override string ToString(){ return string.Format("Car({0},{1})",Make,Color); }
}
Now, you can access it in the same way that you use an enum
. For example,
void Main(){
var blackBeamer = Car.carC;
Console.WriteLine("carC is a " + blackBeamer.Color +" " + blackBeamer.Make);
}
Upvotes: 3
Reputation: 75306
A little trick to make Car
works as enum
, define:
internal enum Maker
{
Ford, Bmw, Toyota,
}
internal enum Color
{
Red, Black, White
}
Then build struct Car
:
public struct Car
{
private readonly Maker _maker;
private readonly Color _color;
public static Car CarA = new Car(Maker.Ford, Color.Red);
public static Car CarB = new Car(Maker.Bmw, Color.Black);
public static Car CarC = new Car(Maker.Toyota, Color.White);
private Car(Maker maker, Color color)
{
_maker = maker;
_color = color;
}
public static bool operator ==(Car car1, Car car2)
{
return car1._maker == car2._maker && car1._color == car2._color;
}
public static bool operator !=(Car car1, Car car2)
{
return !(car1 == car2);
}
}
So, you can use:
Car a = Car.CarA;
bool flag = a == Car.CarB;
Upvotes: 1
Reputation: 28302
The value of an enum is always represented by integers. You can not use a different type (like string arrays).
You could do the following to get similar results:
Dictionary<Car, string[]> cars;
cars = new Dictionary<Car, string[]>();
cars.Add(Car.carA, new string[]{"ford", "red"});
cars.Add(Car.carB, new string[]{"bmw", "black"});
cars.Add(Car.carC, new string[]{"toyota", "white"});
However, you should only do this if you have a requirement to map enums to strings like this. You seem to be mixing various kinds of "things", namely makes and colors of cars. You should think of something more like:
enum Make {
Ford,
BMW,
Toyota
}
enum Color {
Red,
Black,
White
}
and represent the cars as:
struct Car {
Make make;
Color color;
public Car(Make m, Color c) { make = m; color = c; }
}
and the list as:
Car[] cars = new Car[]{new Car(Make.Ford, Color.Red), new Car(Make.BMW, Make.Black), new Car(Make.Toyota, Make.White)};
Upvotes: 1
Reputation: 100527
Another approach is Flags enum:
[Flags]
enum Car
{
None = 0,
ModelFord = 1,
ModelBmw = 2,
ModelToyota = 4,
ColorRed = 8,
ColorBlack = 16,
carA = ModelFord | ColorRed,
carB = ModelBmw | ColorBlack,
carC = ModelToyota | ColorBlack
}
Note that this just sample - you should avoid mixing types of properties in single enum (Car model and color in this case).
Upvotes: 1
Reputation: 150108
No, the C# language does not allow for that.
You can create a
Dictionary<Car, List<string>> cars;
You would add entries to it like
cars = new Dictionary<Car, List<String>>();
cars.Add(Car.carA, new List<String>() { "ford", "red" });
Note though that if you are mixing the concept of "ford" and "red", you may want to consider creating an object to represent that thing, e.g.
public class CarDetails
{
public string Maker { get; set; }
public string Color { get; set; }
}
Then, your Dictionary
object would look like
Dictionary<Car, CarDetails> cars;
cars = new Dictionary<Car.carA, CarDetails>();
cars.Add(Car.carA, new CarDetails() { Maker = "ford", Color = "red" });
Upvotes: 4
Reputation: 245399
No.
First of all, enums in C# are really integer values, not strings.
Second of all, each value in an enum can only have a single value.
You could specify the integer values for each enum value though, which would allow multiple elements in the enum to have the same integer value:
public enum Car
{
Ford = 1,
Red = 1,
Bmw = 2,
Black = 2
// etc.
}
But it sounds like what you're really looking for is a Dictionary.
Upvotes: 2