0

I apologize for this rather fundamental question, however, I could find no documentation. Perhaps because I do not know the proper terminology.

Class structure:

class D{}

abstract class A<T>{}
class B<T> : A<T> {}
class C : B<D> {}

I attempt to create a factory method returning C, while the overall return type must be A. Unfortunately this implementation will produce a compiletime error, altough the inheritance structure seems to be consummately covariant.

public A<T> FactoryMethod()
{
   return new C();
}

More specifically, I am trying to implement a factory, able to produce all three classes based on a input value, while the generic capability has to be obtained.

public A<T> FactoryMethod(int i, Type K)
{
   if(i == 1)
      return new A<K>():

   if(i == 2)
      return new B<K>():

   if(i == 3)
      return new C():
}

Update

I have to create three objects as follows.

A<string> first = FactoryMethod(1, string);
A<int> second = FactoryMethod(2, int);
A<int> third = FactoryMethod(3, int);
Nex
  • 421
  • 1
  • 4
  • 17
  • 3
    What error do you get? What is `T`? – SLaks Aug 20 '15 at 19:00
  • T is just the usual generic type letter. However D is a concrete class. – Nex Aug 20 '15 at 19:02
  • What does the compile error say? You can't just put a `T` there unless it means something in that scope. – 31eee384 Aug 20 '15 at 19:03
  • `C` is not generic - it is a `B` and subsequently an `A`. You cannot return it as an `A` since `T` could be anything. – D Stanley Aug 20 '15 at 19:06
  • @Nex: What if `T` is `string`? – SLaks Aug 20 '15 at 19:07
  • I updated the question. I do specifiy T, although not in the return type of the method itself. I hoped this would be a dynamics feature of C#. – Nex Aug 20 '15 at 19:44
  • That looks like a completely different question to me. – 31eee384 Aug 20 '15 at 19:48
  • possible duplicate of [Pass An Instantiated System.Type as a Type Parameter for a Generic Class](http://stackoverflow.com/questions/266115/pass-an-instantiated-system-type-as-a-type-parameter-for-a-generic-class) – shf301 Aug 20 '15 at 19:49
  • Thanks for your reference. This seems almost equal that to my statement of the problem. However I dont see how I can implement this into the factory method, without changing the main method, declaring those three objects. – Nex Aug 20 '15 at 19:59

3 Answers3

2

In the scope of the class containing FactoryMethod, T is meaningless. You need to specify the generic parameter of A in order to use it as a type.

In this case, because C is B<D>, and B<D> is A<D>, then you would use A<D> as the return type.

For your second question, if you make the factory method generic I think you can get what you want.

public class A<T> {}

public class B<T> : A<T> {}

public class C : B<D> {}

public class D {}

public class Test
{
    public static A<T> FactoryMethod<T>(int i)
    {
       if(i == 1)
          return new A<T>();
       if(i == 2)
          return new B<T>();
       if(i == 3)
          return (A<T>)(object)new C();
       return null;
    }

    public static void Main()
    {
        A<string> first = FactoryMethod<string>(1);
        A<int> second = FactoryMethod<int>(2);
        A<D> third = FactoryMethod<D>(3);
    }
}

Because C (A<D>) isn't assignable to some A<T> (the compiler doesn't know that you'll always pass 3 when T is D) this isn't type-safe.

31eee384
  • 2,748
  • 2
  • 18
  • 27
  • Is there anyway else to create a single factory method, able to return all three classes, without the need of specifying T? – Nex Aug 20 '15 at 19:32
  • I've done that in the past by having an interface `IA` so that `A : IA`, and `IA` provides versions of methods that work without a static `T`. But then you start losing type-safety, and it's a tradeoff game. – 31eee384 Aug 20 '15 at 19:52
  • Unfortunately most of the classes passed as type to the factory method will be sealed (e.g. Bitmap) and not extendable. – Nex Aug 20 '15 at 19:55
  • That's not what I meant: `A` is your abstract type, and `IA` is an interface that `A` implements. `IA` could return `object`s instead of `T`s (or something) and you can deal with their type later. – 31eee384 Aug 20 '15 at 20:13
2

C is a sub class of the bound type A<D>. So the following is valid:

public A<D> FactoryMethod()
{
   return new C();
}

since we can safely say that C is a A<D>. However C is not a generic type and cannot be converted to the open type A<T> where T is a generic argument since T could be any type.

However the following is valid:

public A<T> FactoryMethod()
{
   return new B<T>();
}

Since B<T> is an open generic type as well and any B<T> is an A<T>.


Based on your update, you could write your factory method as:

public A<T> FactoryMethod<T>(int i)
{
   if(i == 1)
      return new A<T>():
   if(i == 2)
      return new B<T>():
   if(i == 3) 
      return (A<T>)(object)new C():
      // The cast to object gets rid of compile time checking,
      // but will throw an InvalidCastExceptoin if T is not D
}

This is a bit ugly with that weird hack for case 3. Then you would call it as:

A<string> first = FactoryMethod<string>(1);
A<int> second = FactoryMethod<int>(2);
A<int> third = FactoryMethod<int>(3); // InvalidCastException!
shf301
  • 31,086
  • 2
  • 52
  • 86
  • Is there anyway else to create a single factory method, able to return all three classes, without the need of specifying T? – Nex Aug 20 '15 at 19:32
  • No not directly. How could you create an `A<>` or `B<>` without specifying `T`. – shf301 Aug 20 '15 at 19:39
  • I updated the question. I do specifiy T, although not in the return type of the method itself. – Nex Aug 20 '15 at 19:43
1

Given this:

class D{}

class A<T>{}
class B<T> : A<T> {}
class C : B<D> {}
enum openT
{
    level1, level2
}

I think that you might be looking for this:

public A<T> FactoryMethod<T>(openT i)
{
   if(i == openT.level1)
      return new A<T>():

   if(i == openT.level2)
      return new B<T>():

}

public A<D> FactoryMethod()
{
    return new C():
}

public static void Main()
{
    A<string> first = OpenFactoryMethod<string>(1);
    A<int> second = OpenFactoryMethod<int>(2);
    A<D> third = FactoryMethod();
}

Note that A cannot be abstract since you are trying to construct it.

I'm failing to see what you are really trying to accomplish here though, since C is a closed type, and therefore your factory method will never make sense for it.

UPDATED

The following might be closer to what you are looking for:

public TAofT FactoryMethod<TAofT, T>() where TAofT : A<T>, new()
{
    return new TAofT():
}

public static void Main()
{
    A<string> first = FactoryMethod<A<string>, string>();
    A<int> second = FactoryMethod<B<int>, int>();
    A<D> third = FactoryMethod<C, D>();
}

But the factory method then seems redundant, since you could just do:

public static void Main()
{
    A<string> first = new A<string>();
    A<int> second = new B<int>();
    A<D> third = new C();
}

UPDATE 2

Unless what you really want is this:

public abstract class AEnum<T, T3> where T3 : B<T>, new()
{
    private static Func<A<T>> factoryMethod;

    public static readonly Level1 = new AEnum<T>(()=>new A<T>());
    public static readonly Level2 = new AEnum<T>(()=>new B<T>());
    public static readonly Level3 = new AEnum<T>(()=>new T3());

    protected AEnum(Func<A<T>> factoryMethod) { this.factoryMethod = factoryMethod; }

    public A<T> New() { return this.factoryMethod(); }
}

used like this:

public class DEnum : AEnum<D, C>
{
}

with:

public static void Main()
{
    A<D> first = DEnum.Level1.New();
    A<D> second = DEnum.Level2.New();
    A<D> third = DEnum.Level3.New();
}

But then you could not mix enum types, since the above is type constrained to D.

Or you could do:

public class OpenAEnum<T, T3> : AEnum<T, T3> where T3 : B<T3>
{
}

public class CInt : B<int> {}
public class Cstring : B<string> {}

with:

public static void Main()
{
    A<string> first = OpenAEnum<string, CString>.Level1.New();
    A<int> second = OpenAEnum<int, CInt>.Level2.New();
    A<D> third = OpenAEnum<D, C>.Level3.New();
}

What is it that you are trying to do?

Tyree Jackson
  • 2,588
  • 16
  • 22