类型推理如何与重载泛型方法一起工作

我有这些类:

/* 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小提琴。

问题

  • 为什么第二种方法没有被调用,因为两个泛型类型的约束匹配并且可以被推断出来?
  • 我怎样才能重写我的第二个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时,这个方法名应该是可以接受的。 我想这个解决方案应该可以接受所有的实际目的。

    链接地址: http://www.djcxy.com/p/51783.html

    上一篇: How does type inference work with overloaded generic methods

    下一篇: Why doesn't C# infer my generic types?