C#通用工厂方法

时间:2010-09-20 23:31:27

标签: c# generics factory

也许这是一个简单的新手C#问题,但也就是这样 - 它将是我的其他问题的新突破,这是非常困难的,没有人知道他们的答案。 :)

假设我在C#中有一个泛型类型:

Thing<T>

让我们说我想用静态工厂方法制作一个东西。在Java中,这不是问题:

public static <T> Thing<T> createThing()
{
  return flag ? new Thing<Integer>(5) : new Thing<String>("hello");
}

我如何在C#中执行此操作?感谢。

3 个答案:

答案 0 :(得分:4)

如果要使用许多不同的模板参数之一返回模板化类的实例,一种方法是使用抽象基(或接口):

abstract class UntypedThing { }
class Thing<T> : UntypedThing
{
    public Thing(T t) { }
}

class Foo
{
    public static UntypedThing createThing(bool flag)
    {
        if (flag)
            return new Thing<int>(5);
        else return new Thing<String>("hello");
    }
}

UntypedThing类将包含尽可能多的代码,不依赖于模板类型。理想情况下,Thing类只包含依赖于模板类型的代码。工厂类Foo始终返回前者。

答案 1 :(得分:3)

理论上你可以使用反射来构建正确的泛型类型,但它对你来说是没用的,因为在某些时候你需要将它转换为不太具体的类型。

public class ThingFactory  {
    public object Create(bool flag) {
        Type outputType = null;
        if(flag) {
            outputType = typeof(string);
        } else {
            outputType = typeof(int);
        }
        return Activator.CreateInstance(typeof(Thing<>).MakeGenericType(outputType));
    }
}

正如您所看到的,执行此操作的值大约为零,因为您需要将返回类型强制转换为所需的类型,这意味着确定它的逻辑需要位于Create方法之外。

我会使用Reinderien的方法,并且具有非通用基础。这是最理智和最惯用的方法。

答案 2 :(得分:0)

哦,当我只是想做一些简单的事情时,我遇到了麻烦。

事实证明C# 4 allows this sort of covariance有点像。首先,我必须使Thing成为一个接口并指定“out”泛型参数:

public interface Thing<out T> {...}

但如果我做某些事情,C#就不会让我使用协方差。例如,如果我尝试从界面返回T:

public interface Thing<out T>
{
  public T GetT();

即使我设法与Thing合作,我该怎么办呢?

Thing<object> thing=createThing();

编译器告诉我无法从使用中推断出类型。

假设我说搞整个T事件并使工厂方法返回类型对象的东西:

public static Thing<object> createThing() {...}

很好,但现在我把它放在哪里?

IList<Thing<object>> list=new List<Thing<object>>();
Thing<object> thing=createThing();
list.Add(thing);

是的,我必须说这是一个带有Object类型的Th的列表,因为C#没有通配符类型。

如果这是Java,我只想说:

public class Thing<T> {...}

public static <T> Thing<T> createThing() {...}

List<?> things=new ArrayList<Thing<?>>();
Thing<?> thing=createThing();
things.add(thing);

如果我想通过说T必须属于特殊类型而需要额外的安全性,我会说:

public static <T extends MyBaseType> Thing<T> createThing() {...}

List<? extends MyBaseType> things=new ArrayList<Thing<? extends MyBaseType>>();
Thing<? extends MyBaseType> thing=createThing();
things.add(thing);

然后,当我获得更多信息时,我会弄清楚T是什么。

这一切似乎归结为C#中不完整的通用协方差以及缺少C#通用通配符。 (我仍然认为这不是一个擦除问题。)

那我该怎么办?唯一简单的事情似乎是遵循Reinderien的答案并拆分出一个非通用的基类。

(我想知道在这个非泛型基类中我是否可以使用对象getValue()然后在子类中使用协方差来返回T getValue()?Ack,我已经厌倦了 - 我会离开那一天。)