类型推断如何与重载泛型方法配合使用

本文关键字:泛型方法 重载 类型 | 更新日期: 2023-09-27 18:34:32

我有这些类:

/* Data classes */
public class Data
{
    public int Id { get; set; }
}
public class InfoData<TInfo> : Data
    where TInfo: InfoBase
{
    public TInfo Info { get; set; }
}
/* Info classes */
public abstract class InfoBase
{
    public int Id { get; set; }
}
public interface IRelated
{
    IList<InfoBase> Related {get; set;}
}
public class ExtraInfo : InfoBase, IRelated
{
    public string Extras { get; set; }
    public IList<InfoBase> Related { get; set; }
}

然后我有两个带有此签名的通用方法:

public TData Add<TData>(TData data)
    where TData: Data
public TData Add<TData, TInfo>(TData data)
    where TData: InfoData<TInfo>
    where TInfo: InfoBase, IRelated

现在,当我创建Data类的实例并调用Add方法

// data is of type Data
Add(data);

使用第一个泛型方法,并正确推断泛型类型 Data

但是当我使用更实现的类型对象实例调用相同的方法

// data is of type InfoData<ExtraInfo>
// ExtraInfo is of type InfoBase and implements IRelated
Add(data);

我希望调用第二个通用方法,但令我惊讶的是,事实并非如此。如果我检查第二个泛型类型约束是:

where TData: InfoData<TInfo>
where TInfo: InfoBase, IRelated

第一个匹配,第二个也匹配。如果这有任何区别,这些类型比简单的Data类型更能实现。

工作示例

这是一个有效的.Net小提琴供您使用。

问题

  1. 为什么没有调用第二个方法,因为两个泛型类型约束都匹配并且可以推断?
  2. 如何重写我的第二个Add方法,以便类型推断能够工作,而不必显式提供这些类型来确保使用正确的重载?

编辑

我在 MSDN 文档中找到了第一个问题的答案

编译器可以根据传入的方法参数推断类型参数;它不能仅从约束或返回值推断类型参数。

在我的例子中,第一个泛型类型可以直接从参数推断出来,但第二个更棘手。它不能仅从参数推断。应使用类型约束,但编译器不会对其进行计算。

对于我的第二个问题,我还有一个可能的解决方案,即将一种类型更改为具体并保持另一种通用。

public InfoData<TInfo> Add<TInfo>(InfoData<TInfo> data)
    where TInfo: InfoBase, IRelated

但是我想知道是否有一种更通用/通用的方法来缓解此问题,因此我仍然可以保留两个类型参数,但不知何故将两种类型都作为通用类型?

类型推断如何与重载泛型方法配合使用

假设您的 Add 方法位于名为 DataCollector 的类中;您可以添加接受InfoData<ExtraInfo>并返回相同类型的扩展方法,如下所示。

public static class DataCollectorExtensions
    {
        public static InfoData<ExtraInfo> AddInfoData(this DataCollector dataCollector, InfoData<ExtraInfo> data)
        {
            return dataCollector.Add<InfoData<ExtraInfo>, ExtraInfo>(data);
        }
    }
这样,您只需在扩展方法中指定一次泛型参数

,并在其他地方使用泛型方法,而无需指定泛型参数。

new DataCollector().AddInfoData(new InfoData<ExtraInfo>());

您仍然必须命名除 Add 以外的方法(我已经命名为 AddInfoData(,否则编译器会再次在 DataCollector 类中选取public TData Add<TData>(TData data)方法。由于您实际上正在添加InfoData,因此此方法名称应该是可以接受的。我想这个解决方案对于所有实际目的应该是可以接受的。