views:

121

answers:

2

I have the following code:

using System;
using System.Linq;
using System.Linq.Expressions;

public class Program
{
    public static void Main()
    {
        Descendant d = new Descendant();
        d.TestMethod();
    }
}

public class Base
{
    protected void FigureItOut<TClass, TMember>(Expression<Func<TClass, TMember>> expr)
    {

    }
}

public class Descendant : Base
{
    public void TestMethod()
    {
        FigureItOut(c => c.Name);
    }

    public String Name { get; set; }
}

I get this compiler error message:

The type arguments for method
'Base.FigureItOut<TClass,TMember> 
(System.Linq.Expressions.Expression<System.Func<TClass,TMember>>)'
cannot be inferred from the usage. Try specifying the type arguments explicitly.

If I change the call to FigureItOut to this:

FigureItOut((Descendant c) => c.Name);

Then it works. Is there a way to get the first example to compile by changing the base class instead?

I know that if I make the entire Base class generic, like this:

public class Base<TDescendant>
{
    protected void FigureItOut<TMember>(Expression<Func<TDescendant, TMember>> expr)
    {

    }
}

public class Descendant : Base<Descendant>
{
    public void TestMethod()
    {
        FigureItOut(c => c.Name);
    }

    public String Name { get; set; }
}

Then it works, but I'd rather not do that, any other hacks that can be employed, perhaps on the method level (ie. change FigureItOut somehow).

+4  A: 

How about an extension method that calls the actual (protected internal) implementation? The only downside is you have to add this..

This works because the source parameter (via this) infers a type for TClass.

public class Base
{
    protected internal void FigureItOut<TClass, TMember>(Expression<Func<TClass, TMember>> expr)
    {
        Debug.WriteLine("Got to actual method");
    }
}

public static class BaseExt
{
    public static void FigureItOut<TClass, TMember>(this TClass source, Expression<Func<TClass, TMember>> expr)
        where TClass : Base
    { // call the actual method
        Debug.WriteLine("Got to extension method");
        source.FigureItOut(expr);
    }
}
public class Descendant : Base
{
    public void TestMethod()
    {
        this.FigureItOut(c => c.Name);
    }

    public String Name { get; set; }
}


As an alternative (if the internal is a pain), consider making it static, with an instance argument that is used primarily for type inference:

protected static void FigureItOut<TClass, TMember>(TClass source, Expression<Func<TClass, TMember>> expr)
{

}

public void TestMethod()
{
    FigureItOut(this, c => c.Name);
}
Marc Gravell
That's thoroughly evil. Love it.
Jon Skeet
@Jon - mwahahahaah
Marc Gravell
Hmm, this solves another problem, related to some diamond-shaped inheritance problem, but it also exposes the FigureItOut method to outsiders, we'll have to consider if that is a tradeoff we can live with.
Lasse V. Karlsen
@lassevk - well, internal outsiders... note that you could make BaseExt internal too...
Marc Gravell
Problem is, and know I didn't state this in the question, but the Base class is in a class library, and the descendant is in another, or in an application project, so there's still some issues here
Lasse V. Karlsen
@lassevk - in that case, consider just a protected static method on Base that accepts an instance as the first argument, and use FigureItOut(this, c=>c.Name) - almost as good.
Marc Gravell
A: 

Unless it takes a parameter, it cant be inferred. Unless it assigns a return value, it cant be inferred.

leppie
Don't forget that arg0 (aka "this") is *also* a parameter that can drive type inference, especially via extension methods.
Marc Gravell
AH yes, that's true :)
leppie