0

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));
TheCloudlessSky
  • 18,608
  • 15
  • 75
  • 116

1 Answers1

4

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);
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I just figured this out too. I just realized that I needed to update my question to replicate my problem. See my edit above. – TheCloudlessSky Jun 28 '10 at 14:28
  • Esentially the problem is that I'm actually passing an `Expression> expression` as the param. – TheCloudlessSky Jun 28 '10 at 14:42
  • @TheCloudlessSky: I'm afraid without seeing more details, it's hard to know what to recommend. – Jon Skeet Jun 28 '10 at 14:47
  • Sorry for the late reply, does my edit make sense? I'm building off of a response you gave me Friday regarding adding Ordering to a repository as a parameter (http://stackoverflow.com/questions/3119537/repository-with-orderby/3119608#3119608). – TheCloudlessSky Jun 28 '10 at 15:29
  • @TheCloudlessSky: I've edited my answer. Hope it helps a bit. – Jon Skeet Jun 28 '10 at 16:18
  • @Jon: Great, all I needed was clarification that it can't be done because of the expression. I like the original idea of having the type for the class name. Thanks! – TheCloudlessSky Jun 28 '10 at 16:29