views:

51

answers:

1

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));
+4  A: 

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
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
Esentially the problem is that I'm actually passing an `Expression<Func<T, TKey>> expression` as the param.
TheCloudlessSky
@TheCloudlessSky: I'm afraid without seeing more details, it's hard to know what to recommend.
Jon Skeet
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
@TheCloudlessSky: I've edited my answer. Hope it helps a bit.
Jon Skeet
@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