简体   繁体   中英

Type inference problem when writing a generic extension method with more than one type

I am writing a generic extension method for IEnumerable for mapping a list of objects to another list of mapped objects. This is how I would like the method to work:

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

This is the method:

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

However articles.Map<ArticleViewModel>(_mappingEngine); gives a compile error. The problem is that the type inference for T1 doesn't work. I have to explicitly call it like this instead:

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

If I create an extension method with only one parameter T1 like this:

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

Then I can call it like this, without having to specify T1:

articles.DummyMap(_mappingEngine);

Is there are reason why the compiler can't infer the type of T1 in the extension method for Map?

The problem is that the type inference for T1 doesn't work

Actually, the problem isn't T1 - it is T2; return types are not used in this inference. So you can't do that. One option might be a fluent API, for example:

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

with something like:

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>());
    }
}

assuming that the interface is something like:

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

I believe you want C# compiler to infer type of T1 even if you provide T2 without providing T1.

The point is you can use type inference, or just don't use it. You can't mix both worlds:

    public void M<T, S>(T t, S s)
    {
    }

    M<string>("hello", "world!")

This code doesn't compile, but:

    public void M<T, S>(T t, S s)
    {
    }

    M("hello", "world!")

..compiles.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM