0

The problem I'm actually working on is related to mappers in ASP.NET MVC but that's way too complex to post on SO, so I've simplified the issue I'm having below. I'll post my code first as it's easier to explain what I'm trying to achieve after the code.

Supporting Code

public abstract class BaseFoo
{
    public int CommonProperty { get; set; }
}

public class Foo1 : BaseFoo
{
    public int SomeProperty { get; set; }
}

public class Foo2 : BaseFoo
{
    public int AnotherProperty { get; set; }
}

public interface IMyInterface<T>
{
    void SomeMethod(T t);
}

public abstract class BaseClass<T> : IMyInterface<T>
    where T : BaseFoo
{
    public virtual void SomeMethod(T t)
    {
        t.CommonProperty = 1;
    }
}

public class ConcreteClass1 : BaseClass<Foo1>
{
    public override void SomeMethod(Foo1 t)
    {
        t.SomeProperty = 57;
        base.SomeMethod(t);
    }
}

public class ConcreteClass2 : BaseClass<Foo2>
{
    public override void SomeMethod(Foo2 t)
    {
        t.AnotherProperty = 123;
        base.SomeMethod(t);
    }
}

public static class ConcreteClassFactory
{
    public enum ConcreteClassType
    {
        ConcreteClass1,
        ConcreteClass2
    }

    public static dynamic CreateClass(ConcreteClassType type)
    {
        dynamic toReturn = null;

        switch (type)
        {
            case ConcreteClassType.ConcreteClass1:
                toReturn = new ConcreteClass1();
                break;
            case ConcreteClassType.ConcreteClass2:
                toReturn = new ConcreteClass2();
                break;
            default:
                break;
        }

        return toReturn;
    }
}

What I want to do is dynamically create different ConcreteClasss and call SomeMethod on that created object, basically I want to pass around my ConcreteClasss as BaseClass, much like you can pass around Foos as BaseFoo. I've gotten it to work with the following code:

class Program
{
    static void Main(string[] args)
    {
        BaseFoo foo = new Foo1();

        dynamic bar = ConcreteClassFactory.CreateClass(ConcreteClassFactory.ConcreteClassType.ConcreteClass1);

        bar.SomeMethod(foo as dynamic);
    }
}

However, this seems very kludgy to cast to a dynamic (also I don't fully understand why removing as dynamic throws a RuntimeBinderException, if someone can explain what's going on that would be appreciated). Is there a better way to achieve what I'm trying to do here?

2
  • What do you want to happen when someone passes a Foo2 in to ConcreteClass1? Commented Jul 6, 2016 at 15:18
  • I realize my original code didn't show why I wanted to have ConcreteClass1 to use Foo1 as it's parameter. @ScottChamberlain if ConcreteClass1 were to get Foo2 it should throw an error. Obviously I could get rid of the generics and have SomeMethod(BaseFoo t) but then in each derived class I'd need to try and cast to t to the type it expects, not terrible but not ideal for my purposes Commented Jul 6, 2016 at 15:22

1 Answer 1

1

With the constraints you have what I would do would be to cast and throw errors from inside the overridden SomeMethod.

public abstract class BaseClass : IMyInterface<BaseFoo>
{
    public virtual void SomeMethod(BaseFoo t)
    {
        t.CommonProperty = 1;
    }
}

public class ConcreteClass1 : BaseClass
{
    public override void SomeMethod(BaseFoo t)
    {
        if(t == null)
            throw new ArgumentNullException(nameof(t));

        var foo1 = t as Foo1;
        if(foo1 == null)
            throw new NotSupportedException($"{nameof(ConcreteClass1)} does not support types other than {nameof(Foo1)}");

        foo1.SomeProperty = 57;
        base.SomeMethod(foo1);
    }
}

public class ConcreteClass2 : BaseClass
{
    public override void SomeMethod(BaseFoo t)
    {
        if (t == null)
            throw new ArgumentNullException(nameof(t));

        var foo2 = t as Foo2;
        if (foo2 == null)
            throw new NotSupportedException($"{nameof(ConcreteClass2)} does not support types other than {nameof(Foo2)}");

        foo2.AnotherProperty = 123;
        base.SomeMethod(foo2);
    }
}

public static class ConcreteClassFactory
{
    public enum ConcreteClassType
    {
        ConcreteClass1,
        ConcreteClass2
    }

    public static BaseClass CreateClass(ConcreteClassType type)
    {
        BaseClass toReturn = null;

        switch (type)
        {
            case ConcreteClassType.ConcreteClass1:
                toReturn = new ConcreteClass1();
                break;
            case ConcreteClassType.ConcreteClass2:
                toReturn = new ConcreteClass2();
                break;
            default:
                break;
        }

        return toReturn;
    }
}

Used like

class Program
{
    static void Main(string[] args)
    {
        BaseFoo foo = new Foo1();

        var bar = ConcreteClassFactory.CreateClass(ConcreteClassFactory.ConcreteClassType.ConcreteClass1);

        bar.SomeMethod(foo);
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.