2008-11-04 29 views
39

En general, cuando se utiliza el operador condicional, aquí es la sintaxis:¿Cómo puedo asignar un Func <> condicionalmente entre lambdas usando el operador ternario condicional?

int x = 6; 
int y = x == 6 ? 5 : 9; 

No es nada especial, bastante sencillo.

Ahora, intentemos usar esto cuando asignamos un Lambda a un tipo de Func. Me explico:

Func<Order, bool> predicate = id == null 
    ? p => p.EmployeeID == null 
    : p => p.EmployeeID == id; 

Esa es la misma sintaxis, y debe trabajo? ¿Derecha? Por alguna razón, eso no. El compilador da este bonito mensaje críptico:

de error 1 Tipo de expresión condicional no se puede determinar porque no hay una conversión implícita entre 'expresión lambda' y 'expresión lambda'

luego me fui por delante y cambiado la sintaxis y de esta manera se hizo trabajo:

Func<Order, bool> predicate = id == null 
    ? predicate = p => p.EmployeeID == null 
    : predicate = p => p.EmployeeID == id; 

Tengo curiosidad de por qué no funciona el primer camino?

(Nota al margen: Terminé no necesitar este código, como descubrí que al comparar un valor int contra nula, sólo tiene que utilizar Object.equals)

Respuesta

40

Puede convertir una expresión lambda para un objetivo particular delegar tipo, pero para determinar el tipo de expresión condicional, el compilador necesita saber el tipo de cada uno de los operandos segundo y tercero. Si bien ambos son solo "expresión lambda", no hay conversión de uno a otro, por lo que el compilador no puede hacer nada útil.

No se recomienda usar una asignación, sin embargo - un reparto es más evidente:

Func<Order, bool> predicate = id == null 
    ? (Func<Order, bool>) (p => p.EmployeeID == null) 
    : p => p.EmployeeID == id; 

Tenga en cuenta que sólo es necesario dotarla de un operando, por lo que el compilador puede realizar la conversión de la otra expresión lambda

+0

me pregunto, si el compilador puede inferir lo siguiente: 'Func predicado = p => p.EmployeeID == id', ¿cómo es que tiene problemas para inferir lo siguiente:' Func predicado = id == null ? (Func ) (p => p.EmployeeID == null) : p => p.EmployeeID == id; '? Quiero decir que conoce el tipo que se necesita para el segundo y tercer operandos a través de la declaración de 'predicado'. – GDS

+0

@GDS: hay una conversión implícita de la expresión lambda al tipo de delegado, por lo que la primera versión funciona. Pero la declaración de 'predicado' no afecta la inferencia de tipo para la expresión condicional. La especificación del lenguaje básicamente dice que el tipo de expresión condicional debe ser deducible solo a través de los operandos. –

+0

Entonces me pregunto, ¿por qué el operador ternario tendría tal requisito? Es solo una asignación condicional del segundo o tercer operando a una variable o una evaluación condicional de una expresión. A menos que me falta algo, cualquier inferencia lograda con una asignación o evaluación directa debería ser plausible con la asignación o evaluación condicional también. Además, si un operante es ambiguo, la inferencia del otro podría resolver la ambigüedad. Si la ambigüedad no se puede resolver, el compilador puede presentar una queja como en el ejemplo anterior. Tal vez una característica omitida ...? – GDS

6

El compilador de C# no puede inferir el tipo de la expresión lambda creada porque procesa el ternario primero y luego la asignación. también se puede hacer:

Func<Order, bool> predicate = 
    id == null ? 
     new Func<Order,bool>(p => p.EmployeeID == null) : 
     new Func<Order,bool>(p => p.EmployeeID == id); 

pero eso sólo chupa, también se podría tratar

Func<Order, bool> predicate = 
    id == null ? 
     (Order p) => p.EmployeeID == null : 
     (Order p) => p.EmployeeID == id; 
+2

Esto último no funciona, porque el compilador no sabe si convertir a un delegado o un árbol de expresiones (o, por ejemplo, un Func que también estaría bien). –

0

Déjame tener mi propio ejemplo, ya que tenía el mismo problema, también (con la esperanza de que el ejemplo ser útil para los demás):

Mi método Find es el método genérico que obtiene Expression<Func<T, bool>> como predicado y da List<T> como salida.
Quería encontrar países, pero los necesito todos si la lista de idiomas estaba vacía, y la lista filtrada, si la lista de idiomas estaba completa. Primero usa el Código de la siguiente manera:

var countries= 
Find(languages.Any() 
    ? (country => languages.Contains(country.Language)) 
    : (country => true)); 

Pero exactamente, me sale el error: there is no implicit conversion between lambda expression and lambda expression.

El problema era que, tenemos sólo dos expresiones lambda aquí, y nada más, por ejemplo, lo que es country => true exactamente? Tenemos que determinar el tipo de al menos una de las expresiones lambda. Si solo se determina una de las expresiones, entonces se omitirá el error. Sin embargo, para hacer el código más legible, extraje ambas expresiones lambda, y se utiliza la variable en lugar, de la siguiente manera:

Expression<Func<Country, bool>> getAllPredicate = country => true; 
    Expression<Func<Country, bool>> getCountriesByLanguagePredicate = country => languages.Contains(country.Language); 

    var countries= Find(languages.Any() 
         ? getCountriesByLanguagePredicate 
         : getAllPredicate); 

que hacen hincapié en que, si me determiné a uno de tipo de la expresión, se solucionará el error.

Cuestiones relacionadas