在运行时通过泛型类型选择正确的重载扩展方法

Pick a correct overloaded extension method by generic type in the runtime

我有多个具有不同泛型类型的相同扩展方法

public static class ExtA 
{
    public static IEnumerable<T> Convert<TQueryType, T>(this IQueryable<TQueryType> source)
        where TQueryType : Personnel where T : IPersonnelView
    {
        //body
    }
}

public static class ExtB 
{
    public static IEnumerable<T> Convert<TQueryType, T>(this IQueryable<TQueryType> source)
        where TQueryType : Employee where T : IEmployeeView
    {
        //body
    }
}

我可以通过发送不同的泛型来选择正确的扩展名

下面的代码选择 ExtA.Convert() :

            return await _context.Personnel.Where(...)
                .Convert<Personnel , IPersonnelView>()
                .FirstOrDefaultAsync();

而这个选择 ExtB.Convert() :

            return await _context.Personnel.Where(...)
                .Convert<Employee , IEmployeeView>()
                .FirstOrDefaultAsync();

一切正常

问题: 现在我有了这样的 ToPagedList 方法:

        async Task<PagedList<TResultType>> ToPagedList<TQueryType, TResultType>(
                IQueryable<TQueryType> queryable)
        {
            //Pagging sorting ...

           //error occured here
            queryable.Convert<TQueryType, TResultType>();
        }

我知道了error

The type 'type1' cannot be used as type parameter 'name' in the generic type or method 'name'. There is no boxing conversion or type parameter conversion from 'type1' to 'type2

我想这样称呼

ToPagedList<Personnel, IPersonnelView>(query)

ToPagedList<Employee, IEmployeeView>(query)

我需要一个解决方案来选择正确的扩展方法而不指定泛型类型

如果其中一个泛型参数有约束,那么使用它的新泛型 class 也必须有相同的约束。正如此处所述: [https://docs.microsoft.com/en-us/dotnet/csharp/misc/cs0314?f1url=%3FappId%3Droslyn%26k%3Dk(CS0314)]

因此,当您调用 queryable.Convert<TQueryType, TResultType>(); 时,方法 ToPagedList 必须具有这样的约束:

async Task<PagedList<TResultType>> ToPagedList<TQueryType, TResultType>(IQueryable<TQueryType> queryable) 
        where TQueryType : Personnel where TResultType : IPersonnelView

但是,当然这会限制它只能与 ExtA.Convert() 方法一起使用,您将需要添加另一个方法 ToPagedList,该方法将使用适合 ExtB.Convert() 的约束(同样,您有多个具有相同签名的 Convert 方法 - 作为单独 classes 中的扩展方法)。这会产生另一个问题——代码重复。

解决此问题的更好方法可能是,首先 - 从共同祖先(例如 IView)派生 IPersonnelViewIEmployeeView,以及 PersonnelEmployee 来自另一个(例如 IPerson):

public interface IView { }
public interface IPersonnelView : IView { }
public interface IEmployeeView : IView { }

public interface IPerson { }
public class Personnel : IPerson { }
public class Employee : IPerson { }

那么可以通过以下方式修改ToPagedList的签名:

async Task<PagedList<TResultType>> ToPagedList<TQueryType, TResultType>(IQueryable<TQueryType> queryable) 
        where TQueryType : IPerson where TResultType : IView

并且您必须修改对 ToPagedList 中方法的调用,添加类型检查:

IEnumerable<IView> conversionResult;
if (typeof(TQueryType) == typeof(Personnel))
    conversionResult = ((IQueryable<Personnel>) queryable).Convert<Personnel, IPersonnelView>();
else if (typeof(TQueryType) == typeof(Employee)) 
    conversionResult = ((IQueryable<Employee>) queryable).Convert<Employee, IEmployeeView>();

我想说这两种解决方案都远非最佳,但它们可以解决问题。要找到一个最佳的,您可能需要稍微重构一下程序的架构。