Leo
Leo

Reputation: 318

Create an instance of an unknown type

Am pretty sure there is a few different ways of doing this, as i got very close to it without success with stuff like Activator.CreateInstance, but i wanted to see your opinion on my specific case.

Is there a way to avoid this kind of switches?

Resource r = null; < --Resource is the base class of block,rock,plant types.

switch (type) //type is a byte you get from somewhere on runtime
{
        //Then you instantiate that class type depending on that "type byte",
        //simply by comparing it to an enum lets say

    case ResourceType.Block:
        r = new Block();
        break;
    case ResourceType.Rock:
        r = new Rock();
        break;
    case ResourceType.Plant:
        r = new Plant();
        break;
 }

//Then you apply some data to that newly created 'resource"
r.id = id;

//Then you save that 'resource' into a dictionary of resources.
ResourceDatabase.Add(r.id, r);

Upvotes: 2

Views: 927

Answers (3)

Enigmativity
Enigmativity

Reputation: 117029

My approach would be to use a dictionary to build up the mapping. The dictionary can be modified at run-time and can be dynamically populated using reflection if need be.

var factory = new Dictionary<ResourceType, Func<Resource>>()
{
    { ResourceType.Block, () => new Block() },
    { ResourceType.Rock, () => new Rock() },
    { ResourceType.Plant, () => new Plant() },
};

Resource r = factory[type].Invoke();

Upvotes: 5

antiduh
antiduh

Reputation: 12407

Another approach would be to label your types with attributes, search for all such-labeled types, and then activate the types based on the chosen enumeration value.

public enum ResourceType
{
    Plant,
    Rock,
    Block
}

[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=false)]
public class ResourceDeclAttribute : Attribute
{
    public ResourceDeclAttribute( ResourceType resType )
    {
        this.ResType = resType;
    }

    public ResourceType ResType { get; private set; }
}

public class Resource
{
    // ...
}

[ResourceDecl( ResourceType.Plant )]
public class Plant : Resource
{ 
    // ...
}

[ResourceDecl( ResourceType.Block )]
public class Block : Resource
{
    // ...
}

[ResourceDecl( ResourceType.Rock )]
public class Rock : Resource
{
    // ...
}


public class Loader
{
    private Dictionary<ResourceType, Type> enumToTypeMap;

    public Loader()
    {
        this.enumToTypeMap = new Dictionary<ResourceType, Type>();
    }

    public void Initialize()
    {
        Assembly targetAssembly;

        // Fill in with the right way to identify your assembly. One trick is to have a dummy
        // class in the assemblies that define your types, make a hard reference to those
        // classes, and then use the class's types to find out what assembly they came from.

        targetAssembly = Assembly.GetExecutingAssembly();

        Type[] exportedTypes = targetAssembly.GetExportedTypes();

        foreach( Type candidate in exportedTypes )
        {
            ResourceDeclAttribute attrib = candidate.GetCustomAttribute<ResourceDeclAttribute>();

            if( attrib != null )
            {
                this.enumToTypeMap.Add( attrib.ResType, candidate );
            }
        }
    }

    public Resource Activate( ResourceType resType )
    {
        Type res = this.enumToTypeMap[resType];

        return (Resource)Activator.CreateInstance( res );
    }
}

Upvotes: 2

Orel Eraki
Orel Eraki

Reputation: 12196

You don't have to hold a mapping from enum if the type names are identical, else you can use Dictionary to hold the mapping.

Here is another example using only the exact name of the object type, using Activator.CreateInstance.

using System;

public class Program
{
    public static void Main()
    {
        string dog = "Dog";
        var dogObj = Activator.CreateInstance(Type.GetType(dog)) as Animal;
        string cat = "Cat";
        var catObj = Activator.CreateInstance(Type.GetType(cat)) as Animal;
        Console.WriteLine(dogObj);
        Console.WriteLine(catObj);
    }
}

public abstract class Animal
{
    public override string ToString() { return "Type: " + GetType().Name; }
}

public class Cat : Animal
{
}

public class Dog : Animal
{
}

Upvotes: 2

Related Questions