简体   繁体   中英

Class type inference from static method / caller?

Given the following two classes:

public class ABC
{
    public void Accept(Ordering<User> xyz)
    {
        // Do stuff with xyz...
    }
}

public class Ordering<TEntity>
        where TEntity : class
{
    private readonly Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> Transform;

    private Ordering(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> transform)
    {
        this.Transform = transform;
    }

    public static Ordering<TEntity> By<TKey>(Expression<Func<TEntity, TKey>> expression)
    {
        return new Ordering<TEntity>(query => query.OrderBy(expression));
    }

    public static Ordering<TEntity> ByDescending<TKey>(Expression<Func<TEntity, TKey>> expression)
    {
        return new Ordering<TEntity>(query => query.OrderByDescending(expression));
    }

    public Ordering<TEntity> ThenBy<TKey>(Expression<Func<TEntity, TKey>> expression)
    {
        return new Ordering<TEntity>(query => this.Transform(query).ThenBy(expression));
    }

    public Ordering<TEntity> ThenByDescending<TKey>(Expression<Func<TEntity, TKey>> expression)
    {
        return new Ordering<TEntity>(query => this.Transform(query).ThenByDescending(expression)); 
    }

    public IOrderedQueryable<TEntity> Apply(IQueryable<TEntity> query)
    {
        return Transform(query);
    }

}

Used in the following way:

ABC abc = new ABC();
abc.Accept(Ordering<User>.By(u => u.Id));

Is there any way to infer the type of T like so:

abc.Accept(Ordering.By(u => u.Id));

You can do it, but not in a generic type. Generic type inference like this only occurs for generic methods. Declare a separate non -generic type, with a generic method:

public class XYZ
{
    public static XYZ Action<T, TKey> (TKey key, T element)
    {
        return new XYZ<T>(element);
    }
}

EDIT: Responding to the question edit.

No, you can't do something like this:

abc.Accept(Ordering.By(u => u.Id));

The problem is the inner expression:

Ordering.By(u => u.Id)

What's the type of u here? It could be any class with an Id property. Note that the C# compiler will need to work out the type of this expression before it looks at abc.Accept . Even if abc.Accept only worked for an Ordering<User> , it would fail.

There are three options here:

  • Use a static method in the generic class, specifying the source type argument explicitly, and inferring the key type argument:

     Ordering<User>.By(u => u.Id) 
  • Use a generic method in a non-generic class, specifying both type arguments explicitly:

     Ordering.By<User, string>(u => u.Id) 
  • Use a generic method in a non-generic class, specifying the type of the lambda parameter explicitly, and letting the compiler infer the key type argument:

     Ordering.By((User u) => u.Id) 

Obviously, all of these cases require you to specify the type explicitly somewhere.

One other option which is a little bit weird is relevant if you've typically already got an instance of User (or at least a variable of that type). You can use that as a sort of example, which gets ignored:

public static Ordering<T> By<T,TKey>(Expression<Func<T, TKey>> func, T example)
{
    return By<T, TKey>(func);
}
...

Ordering.By(u => u.Id, dummyUser);

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