tags:

views:

388

answers:

6
+1  Q: 

C# enum addition

greetings, i have the following enum:

    public enum LegalShipTypes : byte
    {
        Frigate = 1,
        Cruiser = 2,
        Destroyer = 3,
        Submarine = 4,
        AircraftCarrier = 5
    }

i was wondering if there is a way to get the total value of enum in any way. for instance this would result in (1 + 2 + 3 + 4 + 5) = 15.

thank you.

+3  A: 

Try the following assuming it's an enum that inherits from System.Int32 (this is the default).

public int Sum(Type enumType) {
  return Enum.GetValues(enumType).Cast<int>().Sum();
}

EDIT didn't notice the OP's question inherits from byte. Here's a version that works with byte

public int Sum(Type enumType) {
  return Enum.GetValues(enumType).Cast<byte>().Select(x => (int)x).Sum();
}
JaredPar
It inherits from Byte (see above), but your answer is still correct so +1 as of course you can easy convert your example!
Rob Ashton
The example in the question actually has `byte` as its underlying type, which would require something like `Cast<byte>().Sum(b => b)` instead.
LukeH
@Jared: Your edit doesn't work. There's no overload of `Sum` that extends `IEnumerable<byte>` so you need to use one of the overloads that takes a `Func<TSource,TSum>` argument. You can just pass `b => b` as the delegate for an implicit conversion to `Int32`.
LukeH
@Luke, thans forgot about that. Switched it to hae the additional cast to int
JaredPar
@Jared: Sorry, that still doesn't work. The `Cast` method performs a box/unbox for value types rather than a conversion, and you can't unbox to a different type. You'll need to pass a delegate into the `Sum` method to perform the conversion.
LukeH
@Luke, no sorries needed. I forgot that Cast() is indeed a cast vs. a conversion for value types. I changed it to be a Select style conversion (and actually verified it works this time ;) )
JaredPar
A: 

See the accepted answer of this similar question:

http://stackoverflow.com/questions/105372/c-how-to-enumerate-an-enum

You could get the values, enumerate through them and just sum the values in the for loop.

Joel Etherton
A: 

A generic version of JaredPar's answer with Luke's correction:

public int Sum<T>(Type enumType) {
    return Enum.GetValues(enumType).Cast<T>().Sum();
}

Call with:

Sum<byte>(typeof(LegalShipTypes));

EDIT: Well, scratch that idea. I suppose:

public int Sum(Type enumType) {
    return Enum.GetValues(enumType).Cast<byte>().Sum();
}

is the answer.

Wesley Wiser
@wawa: Your example doesn't work because there's no overload of `Sum` that extends `IEnumerable<byte>`. You need to use one of the overloads that takes a `Func<TSource,TSum>` argument. You can just pass `b => b` as the delegate for an implicit conversion to `Int32`.
LukeH
@wawa, this does not compile because there is no generic version of Sum available. Only one for specific types
JaredPar
A: 

If you want to be summing enumerator values, wouldn't you be better off using a flagged enumerator?

Paul Manzotti
+2  A: 

If you can edit the enum, and you need their sum in many places, you can put it in the enum itself:

public enum LegalShipTypes : byte {
  Frigate = 1,
  Cruiser = 2,
  Destroyer = 3,
  Submarine = 4,
  AircraftCarrier = 5,
  All = Frigate + Cruiser + Destroyer + Submarine + AircraftCarrier
} 

This makes more sense in flags enums though:

[Flags]
public enum LegalShipTypes : byte {
  Frigate = 1,
  Cruiser = 2,
  Destroyer = 4,
  Submarine = 8,
  AircraftCarrier = 16,
  All = Frigate | Cruiser | Destroyer | Submarine | AircraftCarrier
} 

Or you can just use this:

Enum.GetValues(typeof(LegalShipTypes)).Cast<byte>().Sum(x=>x)

Which returns a decimal.

But this is a more general way to do it (works regardless of the underlying type of the enum):

public decimal Sum(Type enumType) {
  return Enum
    .GetValues(enumType)
    .Cast<object>()
    .Sum(x => (decimal)Convert.ChangeType(x, typeof(decimal)));
}
Jordão
i like the first better as the enum values have to be the way stated.
iEisenhower
I've added a few more options then.
Jordão
+3  A: 

I didn't want to type this up as an answer, because it doesn't answer your question directly, but based on your comment in response to my comment to your question, it merits some explanation.

Enums are meant to be very simple type safe representations of state. If you simply use constants, then you can assign the wrong constants to a value. This prevents you from assigning the wrong type of constant to a field. For example, if you have something that expects DayOfWeek, you can't assign a FileAccess value, even though they are both constants of the same underlying type.

DayOfWeek day = FileAccess.Write; // doesn't work

If you need this type safety and you don't need for your enum to exhibit any other type of behavior, then use an enum. If you are concerned with having your enum do other things as well (such as enumeration, mathematical operations, etc) then you should consider using classes. See my example below.

public class LegalShipTypes
{
    private readonly byte _numericValue;
    private readonly string _text;

    private LegalShipTypes(byte numericValue, string text)
    {
        _numericValue = numericValue;
        _text = text;
    }

    public byte Value { get { return _numericValue; } }
    public string Text { get { return _text; } }

    public static IEnumerable<LegalShipTypes> All
    {
        get
        {
            return new[] { Frigate, Cruiser, Destroyer, Submarine, AircraftCarrier };
        }
    }

    public static readonly LegalShipTypes Frigate = new LegalShipTypes(1, "Frigate");
    public static readonly LegalShipTypes Cruiser = new LegalShipTypes(2, "Cruiser");
    public static readonly LegalShipTypes Destroyer = new LegalShipTypes(3, "Destroyer");
    public static readonly LegalShipTypes Submarine = new LegalShipTypes(4, "Submarine");
    public static readonly LegalShipTypes AircraftCarrier = new LegalShipTypes(5, "Aircraft Carrier");
}

Now you can use it in a typesafe way like this:

public class Fleet
{
    private readonly List<LegalShipTypes> _ships;

    public Fleet()
    {
        _ships = new List<LegalShipTypes>();
    }

    public LegalShipTypes Flagship { get; set; }
    public ICollection<LegalShipTypes> Ships { get { return _ships; } }
}

....

var fleet = new Fleet();
fleet.FlagShip = LegalShipTypes.AircraftCarrier;
var iDoNotKnowWhyYouWouldNeedThisBut = LegalShipTypes.All.Sum(ship => ship.Value);
Console.WriteLine("The flagship is a(n) \"{0}\".", fleet.FlagShip.Text);
if (fleet.FlagShip == LegalShipTypes.AircraftCarrier) // this will work because it's a reference comparison
    Console.WriteLine("This should be true");

As you can see, you still have type safety, but much more flexibility. It is more code, but you won't find yourself working against the limitations of enum. To reiterate, enum is meant to be simple. It's supposed to be simple. If your needs are simple, don't hesitate to use it. If your needs are more complex, there's no shame in using good old fashioned object oriented programming to solve your problem.

EDIT

In light of your last comment response that the byte values represents the number of pegs, I would highly recommend you don't use enums to solve your problem. You'd be (ironically) trying to put a round peg in a square hole.

Michael Meadows
thank you for taking the time to comment in depth. im at planning and modelling stage at the moment so im not sure which approach to take and i will consider you input if i end up coding it your way.
iEisenhower
+1, Good answer.
missingfaktor
i will accept this as the answer as this approach does not limit me to all ships of different sizes. thanks again for your thoughtful response and Bemrose also.
iEisenhower
LegalShipTypes.All.Sum -> is a way to indicate if all ship positions have been hit or not. if yes then winner is declared.
iEisenhower
You might also be able to benefit from impliict operator overlolading... do things like var total = LegalShipTypes.Frigate + LegalShipTypes.Destroyer;
Michael Meadows