【问题标题】:Trying to create C# method that outputs an object of a generic type that's nested inside another generic type尝试创建输出嵌套在另一个泛型类型中的泛型类型对象的 C# 方法
【发布时间】:2021-02-26 00:47:33
【问题描述】:

为了解决这个问题,我目前正在编写一个生成迷宫的方法。

这是所涉及类的(简化)版本:

public interface IAlgorithm<out MazeType> where MazeType : Maze
{
    MazeType GoGenerate();
}
public class AlgorithmBacktrack : IAlgorithm<Maze>
{
    public Maze GoGenerate()
    {
        //Do things
        return new Maze();
    }
}
public class Maze
{
}

public class MazeWithPath : Maze
{
}

我现在想做的是创建助手来调用这个算法:

public class MazeGenerator
{
    public static MAZETYPEGENERIC Generate<AlgorithmType>()
        where AlgorithmType : IAlgorithm<MAZETYPEGENERIC>, new()
    {
        var alg = new AlgorithmType();
        return alg.GoGenerate();
    }
}

我无法开始工作的是 MAZETYPEGENERIC。从理论上讲,C# 可以知道我放在那里的任何 IAlgorithm 接口实现都会配置 MAZETYPEGENERIC。但是 C# 仍然希望我将其作为通用参数添加到方法中。例如:

public static MAZETYPEGENERIC Generate<AlgorithmType, MAZETYPEGENERIC>()
    where AlgorithmType : IAlgorithm<MAZETYPEGENERIC>, new()
    ...

然而,这意味着调用此调用也需要此参数。尽管理论上可以从 AlgorithmType 推断出来。

//Ideal way to call this method:
Maze m = MazeGenerator.Generate<AlgorithmBacktrack>();

//Actual way to call this after adding the additional generic parameter:
Maze m = MazeGenerator.Generate<AlgorithmBacktrack, Maze>();

如果有人对如何完成选项 1(执行此操作的理想方式)有想法,我很乐意看到/听到。

【问题讨论】:

  • 两个泛型参数是不可避免的。除非返回预定义类型就足够了,例如IMaze (public static IMaze Generate&lt;AlgorithmType&gt;()...)

标签: c# .net generics


【解决方案1】:

一种方法是接受IAlgorithm&lt;MAZETYPEGENERIC&gt; 的实例,并让类型推断推断类型参数MAZETYPEGENERIC 。现在你不需要AlgorithmType 类型参数了

Maze m = MazeGenerator.Generate(new AlgorithmBacktrack());

...

public static MAZETYPEGENERIC Generate<MAZETYPEGENERIC>(IAlgorithm<MAZETYPEGENERIC> algorithm)
    where MAZETYPEGENERIC : Maze {
    return algorithm.GoGenerate();
}

如果 C# 接口使用关联类型(如 Swift)而不是泛型,您本可以访问IAlgorithm 的类型参数。因此,这并不是一种语言中完全不可能拥有的功能。如需比较,请参阅this post

【讨论】:

  • 嗯,我创建 MazeGenerator 类的原因是有一个抽象,您不必实例化 Algorithm 类。 (在真正的应用程序中有更多的类。所以如果我这样做,我也可以直接调用算法。
  • @Devedse 好吧,不幸的是,C# 的泛型还不够强大。如果你想这样做,你必须有 2 个类型参数。
【解决方案2】:

如果您只需要“实例化算法类”,我能得到的最接近的方法可能是将调用从Generate 方法移出GoGenerate 方法,如下所示:

static T Generate<T>() where T : IAlgorithm<Maze>, new()
{
    return new T();
}

Maze m = MazeGenerator.Generate<AlgorithmBacktrack>().GoGenerate();
// I made the algorithm class up
MazeWithPath m = MazeGenerator.Generate<AlgorithmBacktrack2>().GoGenerate();

或者如果局部变量m的类型总是Maze,你也可以这样做:

static Maze Generate<T>() where T : IAlgorithm<Maze>, new()
{
    return new T().GoGenerate();
}

也想评论

理论上 C# 可以知道我放在那里的任何 IAlgorithm 接口实现

我希望 C# 不会(就像现在一样)。 T 在您的情况下是 covariant。如果TIAlgorithm&lt;MazeWithPath&gt; 的实现,则MazeMazeWithPathobject 对于返回类型在语义上都是正确的。

编译器无法在不知道代码上下文的情况下选择一个。如果可以,它可能会为你选错一个:

// This is only correct when the compiler chooses MazeWithPath as return type
// It will become invalid if compiler chooses Maze or object
MazeWithPath m = Generate<AlgorithmBacktrack2>();

也就是说,让Generate 方法采用两个通用参数可能更有意义 IMO。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-12
    • 1970-01-01
    • 1970-01-01
    • 2021-12-25
    • 2014-08-02
    • 2014-01-17
    • 2021-09-08
    相关资源
    最近更新 更多