Inferenza di espressione durante l'ereditarietà
-
06-07-2019 - |
Domanda
Ho il seguente codice:
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; }
}
Ricevo questo messaggio di errore del compilatore:
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.
Se cambio la chiamata a FigureItOut a questo:
FigureItOut((Descendant c) => c.Name);
Quindi funziona. C'è un modo per ottenere il primo esempio da compilare modificando invece la classe base?
So che se rendo generica l'intera classe Base, in questo modo:
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; }
}
Quindi funziona, ma preferirei non farlo, qualsiasi altro hack che possa essere impiegato, forse a livello di metodo (ad es. cambiare FigureItOut in qualche modo).
Soluzione
Che ne dici di un metodo di estensione che chiama l'implementazione effettiva ( protetto interno
)? L'unico aspetto negativo è che devi aggiungere questo.
.
Funziona perché il parametro source
(tramite this
) fornisce un tipo per 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; }
}
In alternativa (se il interno
è una seccatura), considera di renderlo statico, con un argomento di istanza usato principalmente per l'inferenza di tipo:
protected static void FigureItOut<TClass, TMember>(TClass source, Expression<Func<TClass, TMember>> expr)
{
}
public void TestMethod()
{
FigureItOut(this, c => c.Name);
}
Altri suggerimenti
Se non accetta un parametro, non può essere dedotto. A meno che non assegni un valore di ritorno, non può essere dedotto.