泛型方法返回泛型派生类型的对象

本文关键字:对象 类型 派生 返回 泛型 泛型方法 | 更新日期: 2023-09-27 18:03:22

我有点纠结于c#中的泛型。考虑这段代码:

public interface A {};
public interface B : A {};
public interface C : A {};
public interface IMyInterface<in T> where T : A {};
public class FirstImpl<T> : IMyInterface<T> where T : B {};
public class SecondImpl<T> : IMyInterface<T> where T : C {};

现在我需要一些工厂,它接受一个类型并返回正确的实现:

public IMyInterface<T> Create<T>() where T : A 
{
    if(typeof(T) is B) {
        return new FirstImpl<T>();
    }
    if(typeof(T) is C) {
        return new SecondImpl<T>();
    }
}

这在两个层面上不起作用。第一个是返回类型。第二个问题是我不能将"T"传递给第一个或第二个实现,因为它们需要更具体的类型。知道怎么解吗?

泛型方法返回泛型派生类型的对象

任何时候您的代码使用泛型类型或方法,并且该代码对泛型类型参数进行某种测试时,您就做错了。(恕我直言,陷入沉思更糟糕。)泛型类型的全部意义在于编写的代码使不依赖于泛型类型

在你的例子中,一个更好的方法是这样的:

static IMyInterface<T> CreateB<T>() where T : B
{
    return new FirstImpl<T>();
}
static IMyInterface<T> CreateC<T>() where T : C
{
    return new SecondImpl<T>();
}

。编写不同的方法来处理每种场景。无论如何,调用者必须知道它已经在处理什么,如果基于约束的方法要起作用的话。因此,使用不同的方法名并不是问题,每个方法名在适当和各自的情况下使用。

如果上面没有解决您的特定场景,请改进问题,以便它包括一个良好的最小化,完整和可验证的代码示例,不仅显示您正在尝试使用的泛型类型,而且还显示它们将被使用的上下文

如果你不介意反射,你可以这样做:

public static IMyInterface<T> Create<T>() where T : A 
{
    if (typeof(B).IsAssignableFrom(typeof(T)))
    {
        var type = typeof(FirstImpl<>);
        var boundType = type.MakeGenericType(typeof(T));
        return (IMyInterface<T>) Activator.CreateInstance(boundType);
    }
    else if(typeof(C).IsAssignableFrom(typeof(T)))
    {
        var type = typeof(SecondImpl<>);
        var boundType = type.MakeGenericType(typeof(T));
        return (IMyInterface<T>) Activator.CreateInstance(boundType);
    }
    throw new ArgumentException("unknown type " + typeof(T).Name);
}

你可以在。net中试试

如果您想保持设计完整,那么最简单的方法就是使用Create<T>()方法(如public IMyInterface<T> Create<T>() where T : A , B, C)添加额外的约束。您可能需要一个默认情况的实现来完成该方法。

public IMyInterface<T> Create<T>() where T : A, B, C
{
    if (typeof(T) is B)
    {
        return new FirstImpl<T>();
    }
    if (typeof(T) is C)
    {
        return new SecondImpl<T>();
    }
    return new DefaultImpl<T>;
}