C#泛型方法选择

wog*_*ggy 9 c# generics

我正在尝试用 C# 编写通用算法,可以处理不同维度的几何实体。

在下面的人为示例中,我有Point2Point3,它们都实现了一个简单的IPoint接口。

现在我有一个GenericAlgorithm调用函数的函数GetDim。根据类型,此函数有多种定义。还有一个为任何实现IPoint.

我最初预计以下程序的输出是 2, 3。然而,它是 0, 0。

interface IPoint {
    public int NumDims { get; } 
}

public struct Point2 : IPoint {
    public int NumDims => 2;
}

public struct Point3 : IPoint {
    public int NumDims => 3;
}

class Program
{
    static int GetDim<T>(T point) where T: IPoint => 0;
    static int GetDim(Point2 point) => point.NumDims;
    static int GetDim(Point3 point) => point.NumDims;

    static int GenericAlgorithm<T>(T point) where T : IPoint => GetDim(point);

    static void Main(string[] args)
    {
        Point2 p2;
        Point3 p3;
        int d1 = GenericAlgorithm(p2);
        int d2 = GenericAlgorithm(p3);
        Console.WriteLine("{0:d}", d1);        // returns 0 !!
        Console.WriteLine("{0:d}", d2);        // returns 0 !!
    }
}
Run Code Online (Sandbox Code Playgroud)

好的,由于某种原因,具体的类型信息在GenericAlgorithm. 我不完全理解为什么会发生这种情况,但很好。如果我不能这样做,我还有什么其他选择?

Jon*_*eet 10

这种方法:

static int GenericAlgorithm<T>(T point) where T : IPoint => GetDim(point);
Run Code Online (Sandbox Code Playgroud)

... 会一直打电话GetDim<T>(T point)。重载决议在编译时执行,在那个阶段没有其他适用的方法。

如果您希望在执行时调用重载解析,则需要使用动态类型,例如

static int GenericAlgorithm<T>(T point) where T : IPoint => GetDim((dynamic) point);
Run Code Online (Sandbox Code Playgroud)

但是,为此使用继承通常是一个更好的主意-在您的示例中,显然您可以只使用单个方法并返回point.NumDims。我假设在你的真实代码中,有一些原因是等价的更棘手,但如果没有更多的上下文,我们无法就如何使用继承来执行专业化提出建议。这些是你的选择:

  • 根据目标的执行时间类型进行专业化的继承(首选)
  • 用于执行时重载解析的动态类型


ser*_*0ne 6

从 C# 8.0 开始,您应该能够为您的接口提供默认实现,而不是需要泛型方法。

interface IPoint {
    int NumDims { get => 0; }
}
Run Code Online (Sandbox Code Playgroud)

实现通用方法和每个IPoint实现的重载也违反了 Liskov 替换原则(SOLID 中的 L)。您最好将算法推送到每个IPoint实现中,这意味着您应该只需要一个方法调用:

static int GetDim(IPoint point) => point.NumDims;
Run Code Online (Sandbox Code Playgroud)