Question

Say you have a method that takes a Generic type, and e.g. an enum eType as well as a concrete object. The Method then determines which method to call based on the enum Type. Each private method being called knows its own return type (instantiates a concrete typed class based on values from the object being passed in). The public method doesn't know beforehand which type it will return.

/// A Generic method to call individual methods depending on the given enum
/// and returns a Generic type.
public T GetSomething<T>(enum eType, Model myClass) where T : class
{
    // Do some common tasks here before calling the private methods
    // ... 
    switch(eType)
    {
        case eType.A:
            return GetMethodA<T>(myClass);
        case eType.B:
            return GetMethodB<T>(myClass);
    }
}

/// A method that returns a ConcreteTypeA object
private T GetMethodA<T>(MyClass myClass) 
{
    ConcreteTypeA cta = new ConcreteTypeA(); 
    cta.X = myClass.X;
    //... etc.
    return cta;
}

/// A method that returns a ConcreteTypeA object
private T GetMethodB<T>(MyClass myClass)
{ 
    ConcreteTypeB ctb = new ConcreteTypeB(); 
    ctb.Y = myClass.Y;
    //... etc.
    return ctb;
}
  • eType = identify which method to call
  • T = Generic return type
  • GetSomething(): Knows nothing of the return type, other than it extends class.
  • GetMethodA(): Gets a MyClass object, initializes, populates, returns ConcreteTypeA

This is based off of a Factory pattern vs. the alternative of calling the individual methods (making them public) so common preparation can be done before the switch case.

Visual studio is saying "Cannot implicitly convert type "ConcreteTypeA" to "T" which is leading to covariance and contravariance but I'm not sure if this is a bad version of the factory pattern.

The main idea is that GetSomething() doesn't know/care about the return type other than it's generic (the places it would be used would have to know which type they want in return). The private methods (MethodA, B, ...) have to deal with concrete types as they do custom object creation. ConcreteTypeA and B don't share the same interface or base class.

The eType enum is to identify which method to call (see Design Patterns: Abstract Factory vs Factory Method) as is generally done in Factories. The generic type T would be to return a known type instead of e.g. an object because the concrete return types don't implement an interface or base class.

The main difference with the Simple Factory is that the actual return "type T" is only known by the caller, and the private methods. The objects returned by GetSomething do NOT have a common base/interface or it wouldn't even need to be generic. enum eType is like the ID in public static Position Get(int id) http://www.dotnetperls.com/factory and just identifies what the main factory method should do.

Is this a bad or wrong approach? Should I just use the individual methods directly (which doesn't seem to precisely follow the Factory pattern)?

The usage would be that different classes need to be initialized here. It would be easy to just make the individual methods public and call them directly. The reason to put everything inside a single method call is to allow common processing since they all need an instance of MyClass for their initialization.

Was it helpful?

Solution 2

The simplest way to fix this is to make your private builder methods non-generic and have them return object. The GetSomething method can then cast the return values to the given type T:

private object MethodA(MyClass myClass) 
{
    ConcreteTypeA cta = new ConcreteTypeA(); 
    //... 
    return cta;
}

public T GetSomething<T>(enum eType, Model myClass) where T : class
{
    switch(eType)
    {
        case Type.A: return (T)MethodA(myClass);
        case Type.B: return (T)MethodB(myClass);
    }
}

OTHER TIPS

OK, thanks for the clarifying comment I think I see what your trying to do here.

Firstly you want to constrain T to the base class of ConcreteTypeA and ConcreteTypeB (lets call it BaseType).

Secondly you'll probably want to remove the enum and decide what method to call via the type parameter T instead (this stops someone calling GetSomething<TypeA>(eType.B, myClass))

Thirdly you don't need to type parameters on MethodA and MethodB, just have them return their common base type.

/// A Generic method to call individual methods depending on the given enum
/// and returns a Generic type.
public T GetSomething<T>(Model myClass) where T : BaseType
{ 
    // Do some common tasks here before calling the private methods
    // ... 
    if(typeof(T) == typeof(ConcreteTypeA))
        return (T)GetMethodA(myClass);
    if(typeof(T) == typeof(ConcreteTypeB))
        return (T)GetMethodB(myClass);
}

You can possibly use GetMethodA(myClass) as T instead.

If you have to use the enum to decided instead of the type parameter then you'll have to remove the generics and just return the base class to be type safe (or accept that the method will sometimes throw InvalidCast when the enum doesn't match T)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top