简体   繁体   中英

How can I get the generic type parameter of the 'this' parameter in an extension method?

I'm trying to convert the following method to an extension method on IEnumerable:

private static IEnumerable<TTarget> MapList<TSource, TTarget>(IEnumerable<TSource> source)
{
    return source.Select(
            element =>
                _mapper.Map<TSource, TTarget>(element)
        ).ToList();
}

Right now it's called like this:

var sourceList = new List<SourceType>();
return MapList<SourceType, TargetType>(sourceList);

But I want to call it like this:

var sourceList = new List<SourceType>();
return sourceList.MapTo<TargetType>();

I have tried doing this:

public static IEnumerable<TTarget> MapTo<TTarget>(this IEnumerable<TSource> source)
{
    return source.Select(
            element =>
                Mapper.Map<TSource, TTarget>(element)
        ).ToList();
}    

But I get type or namespace TSource not found since it's not included in the method's type parameter list. I can make it work like this:

public static IEnumerable<TTarget> MapTo<TSource, TTarget>(this IEnumerable<TSource> source)
{
    return source.Select(
            element =>
                Mapper.Map<TSource, TTarget>(element)
        ).ToList();
}

But this I have to call it like this:

var sourceList = new List<SourceType>();
sourceList.MapTo<SourceType, TargetType>();

Which I feel is not as clear as sourceList.MapTo<TargetType>() .

Is there any way to do what I want?

There's not enough information in the call to fully determine the generic type parameters to pass to MapTo , and C# doesn't support inferring only some of the types. You either have to specify all the types or none of them.

However, you can get around this by redesigning your interface. Here's just one solution:

public sealed class Mappable<TSource>
{
    private readonly IEnumerable<TSource> source;

    public Mappable(IEnumerable<TSource> source)
    {
        this.source = source;
    }

    public IEnumerable<TTarget> To<TTarget>()
    {
        return source.Select(
                element =>
                    Mapper.Map<TSource, TTarget>(element)
            ).ToList();
    }
}

public static class Extensions
{
    public static Mappable<TSource> Map<TSource>(this IEnumerable<TSource> source)
    {
        return new Mappable<TSource>(source);
    }
}

And now you can call it like this:

var sourceList = new List<SourceType>();
var target = sourceList.Map().To<TargetType>();

Alternatively, if you give up on using extension methods, you can do it like this:

public static class MapTo<TTarget>
{
    public static IEnumerable<TTarget> From<TSource>(IEnumerable<TSource> source)
    {
        return source.Select(
                element =>
                    Mapper.Map<TSource, TTarget>(element)
            ).ToList();
    }
}

And call it like this:

var sourceList = new List<SourceType>();
var target = MapTo<TargetType>.From(sourceList);

Neither of these are particularly elegant. It's up to you if you prefer this syntax over fully specifying the generic parameters on each call.

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