61

So I'm creating a game in XNA, C# 4.0, and I need to manage a lot of PowerUps (which in code are all inherited from class "PowerUp"), and to handle back-end management of the PowerUps I currently have an enum, PowerupEffectType, with a value for each child class of PowerUp. Eventually in the code I need to do conversions from PowerupEffectType to the Powerup type (of class Type, achieved usually with typeof([class name])).

Since this is a group project, I want to marry each value of PowerupEffectType to its corresponding class Type as well as possible, i.e.: Not just expect my other programmers to use switch statements to do the conversion manually, and making sure additions/expansions later involve as few changes in as few places as possible. I have a few options for this, and the best I've discovered so far is creating enum pseudo-methods that condense everything down to a single switch statement (99% of what I want), thanks to some tips I found here: http://msdn.microsoft.com/en-us/library/bb383974.aspx

But I'm trying to take it one step further - can I save a Type in an enum? I know you can save enums as a specific type (link: http://msdn.microsoft.com/en-us/library/cc138362.aspx), but Type isn't one of them. The current choices are byte, sbyte, short, ushort, int, uint, long, and ulong. Is there any feasible way to save a convert a Type to any of the above data types and back?

Just to be clear, this is what I WISH I could do, and I'm looking for a way to do:

// (Assuming 'LightningPowerup', 'FirePowerup', and 'WaterPowerup' are
// all declared classes that inherit from a single base class)

public enum PowerupEffectType
{
    LIGHTNING = typeof(LightningPowerup),
    FIRE = typeof(FirePowerup),
    WATER = typeof(WaterPowerup)
}

Is there any way to do this, or am I just overcomplicating a solution to a problem that's already 99% complete?

Thanks in advance!

user1306322
  • 8,561
  • 18
  • 61
  • 122
KeithA45
  • 779
  • 2
  • 8
  • 11
  • I don't know what you're trying to accomplish here as your ultimate goal, but it smells like something you should be accomplishing with a virtual method instead of switching on an enum. Typically the reason you'd want to get a type from an enum as you're doing is if the enum comes from an external source, probably the DB. But, in that case you probably want your switch block to *instantiate* the powerup type instead of giving you the corresponding class object. – Nate C-K Jun 23 '12 at 09:07

7 Answers7

93

You can't do it as the value of an enum, but you could specify it in an attribute:

using System;
using System.Runtime.CompilerServices;

[AttributeUsage(AttributeTargets.Field)]
public class EffectTypeAttribute : Attribute
{
    public Type Type { get; private set; }

    public EffectTypeAttribute(Type type)
    {
        this.Type = type;
    }
}

public class LightningPowerup {}
public class FirePowerup {}
public class WaterPowerup {}

public enum PowerupEffectType
{
    [EffectType(typeof(LightningPowerup))]
    Lightning,
    [EffectType(typeof(FirePowerup))]
    Fire,
    [EffectType(typeof(WaterPowerup))]
    Water
}

You can then extract those attribute values at execution time with reflection. However, I would personally just create a dictionary:

private static Dictionary<PowerupEffectType, Type> EffectTypeMapping =
    new Dictionary<PowerupEffectType, Type>
{
    { PowerupEffectType.Lightning, typeof(LightningPowerup) },
    { PowerupEffectType.Fire, typeof(FirePowerup) },
    { PowerupEffectType.Water, typeof(WaterPowerup) }
};

No need for a special attribute, no need to extract the values with fiddly reflection code.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Interesting... you're right that a dictionary would definitely be the simplest method, I'm just looking to learn some new techniques, so a complication solution might be exactly what I'm looking for. That being said, I didn't know about attributes, and this changes things. Thank you! – KeithA45 Jun 13 '12 at 14:15
  • or create a static dictionary at runtime lazily initialized with reflection over the enum attributes. could be best of both worlds – Adrian Zanescu Aug 16 '12 at 15:38
19

This is not exactly what u are asking for. I find Jon's attribute method the best. But why not wrap it in an extension method?

public Type GetPowerupEffectType(this PowerupEffectType powerEffect)
{
    switch (powerEffect)
    {
        case LIGHTNING:
            return typeof(LightningPowerup);
        case FIRE:
            return typeof(FirePowerup);
        case WATER:
            return typeof(WaterPowerup);
        default:
            return default(Type);
    }
}

And call it:

PowerupEffectType e = PowerupEffectType.WATER;
var t = e.GetPowerupEffectType();
nawfal
  • 70,104
  • 56
  • 326
  • 368
  • That's pretty much what I do now, with the exception that it defaults to "throw new NotImplementedException()", to make sure that if my other developers add an enum, they have to add it to the extension switch statement as well. It's 99% of what I want, I'm just looking for techniques to build it into the enum declaration itself. Still, thank you! – KeithA45 Jun 13 '12 at 14:01
  • 1
    @KeithA45 Yes Keith, thats right. Thats why I say Jon's first method is the best I found. – nawfal Jun 13 '12 at 16:23
16

How about something like this?

You get the type-safety that you do with an enum, plus implicit conversion to Type

public class PowerupEffectType
{
    private readonly Type _powerupType;

    public static implicit operator Type(PowerupEffectType powerupEffectType)
    {
        return powerupEffectType._powerupType;
    }

    private PowerupEffectType(Type powerupType)
    {
        _powerupType = powerupType;
    }

    public static readonly PowerupEffectType LIGHTNING = new PowerupEffectType(typeof(LightningPowerup));
    public static readonly PowerupEffectType FIRE = new PowerupEffectType(typeof(FirePowerup));
    public static readonly PowerupEffectType WATER = new PowerupEffectType(typeof(WaterPowerup));
}
nawfal
  • 70,104
  • 56
  • 326
  • 368
mlorbetske
  • 5,529
  • 2
  • 28
  • 40
  • 2
    This definitely comes closest to what the OP wants. For extra goodness, the [XML doc tag `completionlist`](http://stackoverflow.com/a/102217/1968) can be used to provide enum-style auto-completion for this type – but unfortunately only in VB, not for C#. – Konrad Rudolph Jun 13 '12 at 11:36
  • Impressive! I'm not sure I'll use this, but this certainly taught me a new technique. What's the syntax of getting the Type out of the PowerupEffectType? Can you give a quick code example? – KeithA45 Jun 13 '12 at 13:58
  • 3
    Just cast to 'Type' (Type)PowerupEffectType.FIRE – mlorbetske Jun 13 '12 at 15:01
9

You could use a static Dictionary<PowerupEffectType, Powerup>. I believe that would be the kind of "marriage" you are looking for. It would allow easy enumeration and access.

benjer3
  • 657
  • 6
  • 13
2

You could use only numeric types as by documentation of Microsoft. By default the underlying type of each element in the enum is int. You can specify another integral numeric type by using a colon, as shown in the previous example. For a full list of possible types, see enum. Reference: Enumeration Types

Adil
  • 146,340
  • 25
  • 209
  • 204
2

Sorry, I don't quite follow this, what are you actually trying to achieve; could you give a code excerpt? I'm not sure why you can't just use inheritance here and what an enumeration gives you that type inheritance doesn't. It feels to me like you're presenting the solution rather than the problem, I may be completely wrong, could you clarify how you're planning to use this meta-information?

I'm confused, are you asking for something that tells you the type of an instance of a type/class? You can use an enumeration to store a list of the types of each type that you say, but why do you want to? You say you don't want to have the other programmers use switch statements, but I'm sorry I can't see what benefit you're getting from storing the type information in some enumeration of... the type. Every instance knows what type it is and what it can do.

What will you do with the type information? If the types all inherit from a base type, then presumably they have common functionality that can be specified in an abstract method for any special-case handling, or perhaps return a Null Object where there's nothing to do (or maybe just do nothing). This way you don't need to worry about adding new classes- as they must have the necessary behaviour. I try to avoid Enums except in situations where you're actually enumerating a fixed set of arbitrary values, they are inflexible. Enums have to be maintained, which is very difficult (in practice).

nicodemus13
  • 2,258
  • 2
  • 19
  • 31
0

I actually think you might want to take a look at a dependency injection framework.

It looks like you are trying to have other developers work on different components and then you are trying to integrate them all at the end in one central location in the code base.

A few projects to look at:

Wil
  • 534
  • 4
  • 18