C# - 返回具体类型的通用方法调用方法?

时间:2013-09-16 11:30:40

标签: c# generics covariance contravariance

假设你有一个采用通用类型的方法,例如枚举eType以及具体对象。然后,Method根据枚举类型确定调用哪个方法。被调用的每个私有方法都知道它自己的返回类型(根据传入的对象的值实例化具体类型的类)。公共方法事先不知道它将返回哪种类型。

/// 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 =识别要调用的方法
  • T =通用退货类型
  • GetSomething():除了扩展类之外,不知道返回类型。
  • GetMethodA():获取MyClass对象,初始化,填充,返回ConcreteTypeA

这是基于工厂模式而不是调用各个方法(将它们公开)的替代方法,因此可以在切换案例之前完成常规准备。

Visual Studio说“不能隐式转换类型”ConcreteTypeA“到”T“这导致了协方差和逆变,但我不确定这是否是工厂模式的坏版本。

主要思想是GetSomething()不知道/关心返回类型,而不是它的泛型(它将使用的地方必须知道他们想要的类型)。私有方法(MethodA,B,...)必须在创建自定义对象时处理具体类型。 ConcreteTypeA和B不共享相同的接口或基类。

eType枚举用于标识要调用的方法(请参阅Design Patterns: Abstract Factory vs Factory Method),这通常在工厂中完成。 泛型类型T将返回已知类型而不是例如一个对象,因为具体的返回类型没有实现接口或基类。

与Simple Factory的主要区别在于实际返回“类型T”仅由调用者和私有方法知道。 GetSomething返回的对象没有公共基础/接口,或者甚至不需要是通用的。枚举eType与public static Position Get(int id) http://www.dotnetperls.com/factory中的ID类似,只是标识主工厂方法应该做什么。

这是一种坏的还是错的方法?我应该直接使用单独的方法(似乎没有精确遵循工厂模式)?

用法是需要在此处初始化不同的类。将各个方法公开并直接调用它会很容易。将所有内容放在单个方法调用中的原因是允许常见处理,因为它们都需要一个MyClass实例进行初始化。

2 个答案:

答案 0 :(得分:3)

好的,感谢澄清的评论,我想我看到你在这里尝试做什么。

首先,您希望将T约束到ConcreteTypeA和ConcreteTypeB的基类(我们将其称为BaseType)。

其次你可能想删除枚举并决定通过类型参数T调用什么方法(这会阻止某人调用 GetSomething&lt; TypeA&gt;(eType.B,myClass))< / p>

第三,您不需要在MethodA和MethodB上键入参数,只需让它们返回它们的公共基类型。

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

您可以使用 GetMethodA(myClass)作为T

如果你必须使用枚举来决定而不是类型参数那么你将不得不删除泛型并只返回基类是类型安全的(或接受该方法有时会在枚举时抛出InvalidCast不匹配T)

答案 1 :(得分:1)

解决此问题的最简单方法是使私有构建器方法非泛型并让它们返回对象。然后,GetSomething方法可以将返回值强制转换为给定类型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);
    }
}