【问题标题】:Type inference problem when writing a generic extension method with more than one type编写具有多个类型的泛型扩展方法时的类型推断问题
【发布时间】:2011-03-09 08:59:30
【问题描述】:

我正在为 IEnumerable 编写一个通用扩展方法,用于将一个对象列表映射到另一个映射对象列表。这就是我希望该方法起作用的方式:

IList<Article> articles = GetArticles();
return articles.Map<ArticleViewModel>(_mappingEngine);

这是方法:

public static IEnumerable<T2> Map<T1, T2>(this IEnumerable<T1> list, IMappingEngine engine)
{
    return list.Select(engine.Map<T1, T2>);
}

但是articles.Map&lt;ArticleViewModel&gt;(_mappingEngine); 给出了编译错误。 问题是 T1 的类型推断不起作用。我必须明确地这样称呼它:

articles.Map<Article, ArticleViewModel>(_mappingEngine);

如果我创建一个只有一个参数 T1 的扩展方法,如下所示:

public static IEnumerable<T1> DummyMap<T1>(this IEnumerable<T1> list, IMappingEngine engine)
{
    return list;
}

那我可以这样调用,不用指定T1:

articles.DummyMap(_mappingEngine);

是否有原因导致编译器无法在 Map 的扩展方法中推断 T1 的类型?

【问题讨论】:

    标签: c# generics c#-3.0 extension-methods


    【解决方案1】:

    问题是 T1 的类型推断不起作用

    实际上,问题不是 T1 而是 T2;此推理中不使用返回类型。所以你不能那样做。一种选择可能是流畅的 API,例如:

    return articles.Map(_mappingEngine).To<SomeT2>();
    

    类似:

    public static MapProjection<T1> Map<T1>(this IEnumerable<T1> list, IMappingEngine engine)
    {
        return new MapProjection<T1>(list, engine);
    }
    public class MapProjection<T1>
    {
        private readonly IEnumerable<T1> list;
        private readonly IMappingEngine engine;
        internal MapProjection( IEnumerable<T1> list, IMappingEngine engine)
        {this.list = list; this.engine = engine;}
    
        public IEnumerable<T2> To<T2>()
        {
            return list.Select(engine.Map<T1, T2>());
        }
    }
    

    假设界面是这样的:

    public interface IMappingEngine {
        Func<T1, T2> Map<T1, T2>();
    }
    

    【讨论】:

    • 这是一个很好的解决方案,但是我仍然很好奇为什么推理不适用于我的案例。编译器不应该搜索所有匹配的以 T1 作为第一个参数的通用扩展方法吗?
    • @可以如果指定泛型参数,那就是全有或全无。有关于支持“mumble”在这里输入的讨论,即类似Map&lt;?,ArticleViewModel&gt;(...)(意思是:你知道第一个,我告诉你第二个),但这不是目前的语言。
    • @Marc,我相信我已经回答了他。总之,“为什么?”这只是一个语言实现细节,它不能有什么好的答案,可以吗?
    • 啊,现在说得通了。我在想它也会尝试将 Map 评估为 Map ,但我可以看到这会导致歧义。谢谢!
    • @Can - 存在的问题是:你可以有 Foo()Foo&lt;T&gt;()Foo&lt;T1,T2&gt;() - 它需要特殊的逻辑来弄清楚你的意思。
    【解决方案2】:

    我相信您希望 C# 编译器推断 T1 的类型,即使您提供 T2 而不提供 T1。

    关键是您可以使用类型推断,或者干脆不使用它。你不能混合两个世界:

        public void M<T, S>(T t, S s)
        {
        }
    
        M<string>("hello", "world!")
    

    这段代码不能编译,但是:

        public void M<T, S>(T t, S s)
        {
        }
    
        M("hello", "world!")
    

    ..编译。

    【讨论】:

    • 我希望它推断 T1,而不是 T2。 T1 是该方法所基于的类型(即我的示例中的 IEnumerable
      。)
    • 几乎一样。 T1、T2 或 T1000(不,它不是终结者……)。你不能混合两个世界。您使用类型推断或避免使用它:D
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-04
    • 2018-05-15
    • 2021-03-22
    • 1970-01-01
    相关资源
    最近更新 更多