2008-11-04 7 views
1

je le code suivant:inférence d'expression au cours de l'héritage

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

Je reçois ce message d'erreur du compilateur:

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. 

Si je change l'appel à FigureItOut à ceci:

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

Alors ça marche. Existe-t-il un moyen de compiler le premier exemple en changeant la classe de base à la place?

Je sais que si je fais toute la classe de base générique, comme ceci:

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

Ensuite, cela fonctionne, mais je préfère ne pas faire, d'autres hacks qui peuvent être employés, peut-être sur le niveau de la méthode (c'est-à-dire changer FigureItOut en quelque sorte).

+0

J'ai ajouté une alternative qui évite l'exigence 'internal' (et la méthode d'extension) –

Répondre

6

Que diriez-vous d'une méthode d'extension qui appelle l'implémentation réelle (protected internal)? Le seul inconvénient est que vous devez ajouter this..

Cela fonctionne parce que le paramètre source (via this) infère un type pour 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; } 
} 

Comme alternative (si le internal est une douleur), envisager d'en faire statique, avec un argument d'instance qui est utilisé principalement pour l'inférence de type:

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

} 

public void TestMethod() 
{ 
    FigureItOut(this, c => c.Name); 
} 
+0

Hmm, cela résout un autre problème, lié à un problème d'héritage en forme de diamant, mais il expose également la méthode FigureItOut à des étrangers, nous Je vais devoir considérer si c'est un compromis avec lequel nous pouvons vivre. –

+0

@lassevk - eh bien, les internes internes ... notez que vous pouvez aussi rendre BaseExt interne ... –

+0

Le problème est, et je sais que je n'ai pas indiqué cela dans la question, mais la classe Base est dans une bibliothèque de classes, et la descendant est dans un autre, ou dans un projet d'application, donc il y a encore quelques problèmes ici –

0

À moins qu'il faut une paramètre, il ne peut être déduit. À moins qu'il n'attribue une valeur de retour, il ne peut pas être déduit.

+0

N'oubliez pas que arg0 (alias "this") est aussi un paramètre qui peut conduire l'inférence de type, en particulier via les méthodes d'extension. –

+0

Ah oui, c'est vrai :) – leppie

Questions connexes