简体   繁体   中英

Can an enum return a new instance in C#?

Greetings everyone!

I'll try to make my problem simple: I have an enum to select which ObjType I should use ( ObjTypeA and ObjTypeB both inherits from ObjType ). So I created a method to extend the given enum , in order to return a new instance according to the selected property in the enum , like follows in the code. I think it works more or less like a factory design pattern. So far so good, but eventually, like in the class MyClass , I may attempt to create n instances of ObjTypeA or ObjTypeB , but I'll have to face the if statement everytime I call the GetObjTypeInstance() method. So:

  • Can an enum return an instance, something like: public enum EObjType { ObjTypeA = new ObjTypeA(), ObjTypeB = new ObjTypeB() } ? Actually, it'd be better to append some GetInstance() method to the ObjTypeA and to the ObjTypeB options in the enum . If there's a way to do this, how can I do it? Doing this I'd avoid those if statements every while step.
  • Is there any other (and better) way to this this (if you understood my problem...)? How?

Thanks in advance!

Follow the example code:

public static class EObjTypeExt
{
    public static ObjType GetObjTypeInstance(this EObjType ot)
    {
        if (ot == EObjType.ObjTypeA)
        {
            return new ObjTypeA();
        }
        else if (ot == EObjType.ObjTypeB)
        {
            return new ObjTypeB();
        }
        throw new ArgumentOutOfRangeException("unrecognized type!");
    }
}

public enum EObjType { ObjTypeA, ObjTypeB }

public class MyClass
{
    ObjType[] obj { get; set; }

    public MyClass(EObjType otEnum, int n)
    {
        this.obj = new ObjType[n];
        int i = 0;
        while (i < n)
        {
            this.obj[i] = otEnum.GetObjTypeInstance();
            i++;
        }
    }
}

You'll have to byte this apple somewhere.

Maybe replace the if/elseif chain with switch statement, they work great with enums.

Instead of using an enum , I would use a class that looks like an enum:

public class EObjType {
    public static readonly EObjType ObjTypeA = new EObjType(() => (ObjType)(new ObjTypeA));
    public static readonly EObjType ObjTypeB = new EObjType(() => (ObjType)(new ObjTypeB));

    private readonly Func<ObjType> generator;
    private EObjType(Func<ObjType> generator) {
        this.generator = generator;
    }

    public ObjType GetInstanceOfObjType() {
        return generator();
    }
}

You can then use it exactly as you have been the enum.

EObjType otEnum = EObjType.ObjTypeA;
ObjType obj = otEnum.GetInstanceOfObjType();

You need to use a factory or other creational design pattern.

For instance, you could hold a dictionary from enum key to type value to get the desired class type using selected enum value. Then use reflection to create a new instance (object) of received type.

Initialize static dictionary's values using static constructor of factory class. You can enter the values manually or better yet, load possible values from a config file.

I'm not sure that I'd really advocate this approach, but you could call the enum ToString() method, treat that as your class name and use reflection to instantiate an object of that type.

One advantage of this would be that you could reflect and get the type once, then call the constructor n times in your loop.

As Danny Varod points out, a dictionary mapping your enum values to their Types (or to functions that create those types) would allow you to avoid if statements. Since enum is really just an integer underneath, an array would be more memory and time efficient, but readability is probably most important here.

You could create a factory that allows registration of functions that map to your enumeration, you could that use some sort of registration process to register your different enumerations

public class ObjectFactory
{
    private readonly Dictionary<MyObjectType, Func<MyObject>> _store = new Dictionary<MyObjectType, Func<MyObject>>();

    public void Register<T>(MyObjectType type) where T: MyObject, new()
    {            
        this.Register(type, () => new T());
    }

    public void Register(MyObjectType type, Func<MyObject> factory)
    {
        _store.Add(type, factory);
    }

    public MyObject CreateInstance(MyObjectType type)
    {
        Func<MyObject> factory;
        if(_store.TryGetValue(type, out factory))
        {
            return factory.Invoke();
        }
        return null;
    }
}

public enum MyObjectType { A, B }

public class MyObject {}

public class MyObjectA : MyObject {}
public class MyObjectB : MyObject {}

Usage as follows

var factory = new ObjectFactory();
factory.Register<MyObjectA>(MyObjectType.A);
factory.Register<MyObjectB>(MyObjectType.B);

var a = factory.CreateInstance(MyObjectType.A);
var b = factory.CreateInstance(MyObjectType.B);

Assert.IsInstanceOf(typeof(MyObjectA), a);
Assert.IsInstanceOf(typeof(MyObjectB), b);

You could use Activator.CreateInstance.

public class ObjType {}
public class ObjTypeA : ObjType {}
public class ObjTypeB : ObjType {}
public enum EObjType { ObjTypeA, ObjTypeB }

public static class EObjTypeExt
{
    public static ObjType GetObjTypeInstance( EObjType ot)
    {
        object o = Activator.CreateInstance(null,ot.ToString());
        return (ObjType)o;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM