Exec
Exec

Reputation: 407

How do I create an object with its type defined conditionally?

I have a series of classes each having different properties and each of them having an ID (per-type not per-instance).

Given the following:

public class TestEntity : EntityBase {
    public override ushort ID { get; } = 1;
    public override void something() { do_something(); }
}
public class OtherEntity : EntityBase {
    public override ushort ID { get; } = 2;
    public override void something() { something_else(); }
}

When reading data I only have a ushort:

ushort EntityId = BitConverter.ToUInt16(data.GetRange(CURRENT_POSITION + TILE_ENTITY_ID_OFFSET, TILE_ENTITY_ID_LENGTH).ToArray().Reverse().ToArray(), 0);

How do I use the value of EntityId to create different type of objects based on its value? Using if or switch statements is not an option as there will be more than 200 types.

Upvotes: 2

Views: 958

Answers (3)

Alexander Powolozki
Alexander Powolozki

Reputation: 675

Best way would be to define an Attribute-Subclass containing an ID property and then to annotate all types with the attribute providing a unique id for each type.
Layter you can collect and filter loaded types containing given attribute and filter by the ID property of the attribute.
With this approach you could later add additional subtypes without the need to modify the consuming code.
The implementation could look like:

public sealed class MyCustomAttribute : Attribute
{
    public ushort Id { get; set; }

    public MyCustomAttribute(ushort id)
    {
        this.Id = id;
    }
}

public class MyDemoConsumer
{
    public void MyConsumingMethod(ushort requiredTypeId)
    {
        var requestedType = AppDomain
            .CurrentDomain
            .GetAssemblies()
            .SelectMany(asm => asm.GetTypes())
            .Where(type => type.GetCustomAttributes(typeof(MyCustomAttribute), false).Any())
            .Select(type => new { Type = type, CustomId = type.GetCustomAttributes(typeof(MyCustomAttribute), false).Cast<MyCustomAttribute>().Single().Id })
            .Where(item => item.CustomId == requiredTypeId)
            .Select(item => item.Type)
            .SingleOrDefault();

        if (requestedType != null)
        {
            var result = Activator.CreateInstance(requestedType);
        }
    }
}

Upvotes: 3

Matthew Whited
Matthew Whited

Reputation: 22443

... tons of ways of doing this but here's a simple one ...

class Program
{
    static void Main()
    {
        var simpleFactory = new SimpleFactory();
        var entity = simpleFactory.Create(1);
        entity.Something();
    }
}

public abstract class EntityBase
{
    public abstract ushort ID { get; }
    public abstract void Something();
}

public class TestEntity : EntityBase
{
    public override ushort ID { get { return 1; } }
    public override void something() { }
}
public class OtherEntity : EntityBase
{
    public override ushort ID { get { return 2; } }
    public override void something() { }
}

public class SimpleFactory
{
    private Dictionary<ushort, Func<EntityBase>> config = new Dictionary<ushort, Func<EntityBase>>
    {
        { 1, ()=>new TestEntity()},
        { 2, ()=>new OtherEntity()},
    };

    public EntityBase Create(ushort entityId)
    {
        if (!config.ContainsKey(entityId))
            throw new InvalidOperationException();

        return config[entityId]();
    }
}

Upvotes: 3

Michael Gunter
Michael Gunter

Reputation: 12811

If I understand your question, here's one way to go about it (there are many).

private static Dictionary<ushort, Type> TypeMap = new Dictionary<ushort, Type>()
                                                   {
                                                       { 1, typeof(TestEntity) },
                                                       { 2, typeof(OtherEntity) }
                                                   };

private EntityBase CreateEntity(ushort id)
{
    var type = TypeMap[id];
    return (EntityBase) Activator.CreateInstance(type);
}

Upvotes: 8

Related Questions