简体   繁体   中英

C# generic method type constraint to several classes

I have something like this:

    public List<T> GetPageRows<T>(Expression<Func<T, bool>> predicate, List<ColumnSortOrder> sortOrder, int pageSize, int pageNo) where T : Type1, Type2, Type3
    {
        var filteredQueryable = GetRowsAndFilter(predicate);
        var orderedQueryable = GetOrderedQueryable(sortOrder, filteredQueryable);
        var pagedQueryable = orderedQueryable.Skip((pageNo - 1) * pageSize).Take(pageSize);
        return pagedQueryable.ToList();
    }

    private IQueryable<Type1> GetRowsAndFilter(Expression<Func<Type1, bool>> predicate)
    {
        return GetType1s(predicate);
    }

    private IQueryable<Type2> GetRowsAndFilter(Expression<Func<Type2, bool>> predicate)
    {
        return GetType2s(predicate);
    }

    private IQueryable<Type3> GetRowsAndFilter(Expression<Func<Type3, bool>> predicate)
    {
        return GetType3s(predicate);
    }

I was hoping that by making the non-generic class specific versions of GetRowsAndFilter() and then restricting the generic type parameter for GetPageRows() to one of these three classes, that the compiler was able to figure out what to do.

But where T : Type1, Type2, Type3 seems to be illegal.

How do I solve this?

The idea is that the algorithm in GetPageRows() is general and does not depend on the specific kind of rows I am getting. Only the return type and the predicate depends on T.

EDIT: I tried constraining on their base type and also on a common empty interface, but I still get an error in the var filteredQueryable = GetRowsAndFilter(predicate); . "Cannot resolve method". It doesn't know how to choose the right version of these methods. I don't know what to do. It should be possible to separate the general algorithm in GetPageRows() from the actual type T and the specific, different queries for each T that is in the different versions of GetRowsAndFilter().

Although you can get around multiple constraints using empty interfaces to specify one constraint, this won't solve your issue: not resolving GetRowsAndFilter correctly.

After bending the code a little, I came up with this stub (I changed and cut stuff out to get it to compile, but you should be able to apply the same concept to your code):

    static void Main(string[] args)
    {
       var res = GetPageRows<Type1>(GetRowsAndFilter, t => true, null, 0, 0);
       var res2 = GetPageRows<Type2>(GetRowsAndFilter, t => true, null, 0, 0);

        Console.ReadLine();
    }

    public static List<T> GetPageRows<T>(Func<Expression<Func<T, bool>>, IEnumerable<T>> getRows, Expression<Func<T, bool>> predicate, List<int> sortOrder, int pageSize, int pageNo)
    {
        var filteredQueryable = getRows(predicate);
        return filteredQueryable.ToList();
    }

    private static IEnumerable<Type1> GetRowsAndFilter(Expression<Func<Type1, bool>> predicate)
    {
        return Enumerable.Empty<Type1>();
    }

    private static IEnumerable<Type2> GetRowsAndFilter(Expression<Func<Type2, bool>> predicate)
    {
        return Enumerable.Empty<Type2>();
    }

    private static IEnumerable<Type3> GetRowsAndFilter(Expression<Func<Type3, bool>> predicate)
    {
        return Enumerable.Empty<Type3>();
    }

You need to also provide the method to call otherwise the compiler can't resolve it because it doesn't have enough type information about T , by supplying the method and having it use the same generic type in the method signature, the compiler can at least ensure the methods are talking about the same T . On the calling side ( GetPageRows<Type1>() ), specifying the type is the driver for the rest to resolve correctly.

Since multiple inheritance isn't possible in C#, you can't do that unless at most one of Type1 , Type2 and Type3 are not interfaces. If more than one of Type1 , Type2 , Type3 were not interfaces, it would be impossible to ever satisfy the constraint. Additionally, the interfaces must come last in the ordering of the type constraints.

You might say, wait, what if Type3 : Type2 : Type1 ? In that you case we can still have a T that satifies Type1 but not Type2 nor Type3 . So in this case we gain nothing from writing Type1, Type2, Type3 as it's equivalent to Type3 by itself.

Specifying the three different types explicitly like that is intuitively wrong: how would you know, type-safely, at compile time, which type you're working with? Define a common base class from which they all derive or an interface which they all implement and constrain the generic parameter type to that lowest common denominator.

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