0

对于这个相当基本的问题,我深表歉意,但是,我找不到任何文档。也许是因为我不知道正确的术语。

班级结构:

class D{}

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

我试图创建一个返回 C 的工厂方法,而整体返回类型必须是 A。不幸的是,这个实现会产生编译时错误,尽管继承结构似乎是完全协变的。

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

更具体地说,我正在尝试实现一个工厂,能够根据输入值生成所有三个类,而必须获得通用能力。

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():
}

更新

我必须创建三个对象,如下所示。

A<string> first = FactoryMethod(1, string);
A<int> second = FactoryMethod(2, int);
A<int> third = FactoryMethod(3, int);
4

3 回答 3

2

在包含 的类的范围内FactoryMethodT是没有意义的。您需要指定泛型参数A才能将其用作类型。

在这种情况下,因为CisB<D>B<D>is A<D>,那么您将使用A<D>它作为返回类型。

对于您的第二个问题,如果您将工厂方法设为通用,我认为您可以得到您想要的。

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);
    }
}

因为C( A<D>) 不能分配给某些人A<T>(编译器不知道您将始终传递is ) 3,所以这不是类型安全的。TD

于 2015-08-20T19:06:07.180 回答
2

C是绑定类型的子类A<D>。所以以下是有效的:

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

因为我们可以肯定地说这C是一个A<D>. 但是C,它不是泛型类型,并且不能转换为A<T>T 是泛型参数的开放类型,因为T它可以是任何类型。

但是以下是有效的:

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

因为B<T>也是一个开放的泛型类型,而 anyB<T>是一个A<T>.


根据您的更新,您可以将工厂方法编写为:

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
}

对于案例 3 的那个奇怪的 hack,这有点难看。然后你可以称之为:

A<string> first = FactoryMethod<string>(1);
A<int> second = FactoryMethod<int>(2);
A<int> third = FactoryMethod<int>(3); // InvalidCastException!
于 2015-08-20T19:09:42.540 回答
1

鉴于这种:

class D{}

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

我认为您可能正在寻找这个:

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();
}

请注意, A 不能是抽象的,因为您正在尝试构建它。

不过,我看不到您在这里真正想要完成的工作,因为 C 是一种封闭类型,因此您的工厂方法永远不会对它有意义。

更新

以下可能更接近您正在寻找的内容:

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>();
}

但是工厂方法似乎是多余的,因为你可以这样做:

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

更新 2

除非你真正想要的是:

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(); }
}

像这样使用:

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

和:

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

但是你不能混合枚举类型,因为上面是类型限制为D.

或者你可以这样做:

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

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

和:

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();
}

你想做什么?

于 2015-08-20T22:19:15.883 回答