0

I am using Polymorphism to define different shape classes using a base shape class

public class Shape
{
}

class Circle : Shape
{
}

class Triangle : Shape
{
}

At runtime, I create an instance of the class depending on a variable "thisShape"

Shape myShape;
if (thisShape == "Circle") myShape = new Circle();
if (thisShape == "Triangle") myShape = new Triangle();

This works, but I feel there should be a way to avoid using multiple if's.

# Pseudo code
Shape myShape = new {thisShape}();

Is there a one line syntax to a dynamic instance?

MortimerCat
  • 829
  • 9
  • 26
  • possible duplicate of [c# create an instance of a class from a string](http://stackoverflow.com/questions/223952/c-sharp-create-an-instance-of-a-class-from-a-string) – Shaharyar Aug 11 '14 at 14:17
  • You should be using if, else if, else if - or a switch case. Allowing one if to fall through to the next is inefficient. – Mike Aug 11 '14 at 14:24

2 Answers2

2

In theory there is, but I wouldn't recommend it: You can use reflection for that.

Type shapeType = Assembly.GetExecutingAssembly().GetType(thisShape);
Shape myShape = (Shape)Activator.CreateInstance(shapeType);

EDIT:

This comes with a lot of problems, including performance and security concerns. There are much better ways to deal with such issues, such as the Abstract Factory design pattern.

chris
  • 2,541
  • 1
  • 23
  • 40
  • 1
    I wouldn't recommend it either - especially if "thisShape" is populated by user input. – Mike Aug 11 '14 at 14:17
  • It may not be a so bad idea if "thisShape" is only a keywork linked to the corresponding class (for example thanks to a configuration file). – timothy.B Aug 11 '14 at 14:20
  • 1
    Well, you can certainly work around security concerns. But it isn't easy to get right, so I would stay clear from it. – chris Aug 11 '14 at 14:21
  • 1
    Agreed. There are possibly scenarios where adding this complexity is necessary / warranted / whatever. Doing it just to avoid having an if/else block (by the way a switch case might be prettier) - no. – Mike Aug 11 '14 at 14:24
  • Exactly. Reflection is a powerful tool, but with great power comes great responsibility ;-). – chris Aug 11 '14 at 14:29
1

You should encapsulate this into SimpleShapeFactory.

If you don't want to write if or case, you could create your own Attribute (let name it ShapeNameAttribute):

[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)]
sealed class ShapeNameAttribute : Attribute
{
    #region Constructors

    public ShapeNameAttribute(string name)
    {
        Name = name;
    }

    #endregion // Constructors

    #region Properties

    public string Name
    {
        get;
        set;
    }

    #endregion // Properties
}

Then mark all your classes with this attribute:

[ShapeName("Shape")]
public class Shape
{
}

[ShapeName("Circle")]
class Circle : Shape
{
}

[ShapeName("Triangle")]
class Triangle : Shape
{
}

And create SimpleShapeFactory like this:

static class SimpleShapeFactory
{
    #region Private Members

    public static readonly Type[] _shapes = new Type[]
    {
        typeof(Shape),
        typeof(Circle),
        typeof(Triangle)
    };

    #endregion // Private Members

    #region Methods

    /// <summary>
    /// Creates shape by it's name
    /// </summary>
    /// <param xmlName="name">Name of the shape</param>
    /// <returns>Created shape</returns>
    public static Shape Create(string name)
    {
        foreach(var shape in _shapes)
        {
            var attribute = (ShapeNameAttribute)shape.GetCustomAttributes(typeof(ShapeNameAttribute), true)[0];

            if(attribute.Name == name)
            {
                return (Shape)Activator.CreateInstance(shape);
            }
        }

        throw new ArgumentException("Invalid name");
    }

    #endregion // Methods

}

If you don't want to define allowed types in SimpleShapeFactory, you could, of course, use reflection to determine their, but

Note: Always remember, using reflection may cause performance problems.

awesoon
  • 32,469
  • 11
  • 74
  • 99
  • Nice and clean solution, which I would definitely recommend over mine - at least it mostly solves the security problem. – chris Aug 11 '14 at 14:36
  • I would still stay clear from using reflection for this use case, though, and your solution still uses reflection. – chris Aug 11 '14 at 14:39
  • I agree with you. Reflection can improve readability when there will be a lot of `Shape` classes. For just three classes I would prefer `switch/case` solution. – awesoon Aug 11 '14 at 14:46
  • Obviously, this was just a trivial example to get my query across. The real life scenario involves executing different business rules depending who the customer is. This solution is a great foundation. Thank you. – MortimerCat Aug 11 '14 at 18:22