类型推断如何与重载的泛型方法一起使用

时间:2014-12-18 11:00:15

标签: c# generics type-inference

我有这些课程:

/* 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类型更实现,如果这有任何区别。

工作示例

Here是一个有效的.Net小提琴供你玩。

问题

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

    我在MSDN documentation

    找到了我的第一个问题的答案
      

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

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

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

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

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

1 个答案:

答案 0 :(得分:0)

假设您的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一样,此方法名称应该是可接受的。 我认为这个解决方案应该可以用于所有实际目的。