Search code examples
c#compiler-errorsexpression-treestype-inference

Expression inference during inheritance


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).


Solution

  • 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);
    }