tuple name item create change c# linq c#-7.0

name - ¿Cómo anular la verificación c#7 tupla en la consulta LINQ?



tuples c# (8)

Dado:

class Program { private static readonly List<(int a, int b, int c)> Map = new List<(int a, int b, int c)>() { (1, 1, 2), (1, 2, 3), (2, 2, 4) }; static void Main(string[] args) { var result = Map.FirstOrDefault(w => w.a == 4 && w.b == 4); if (result == null) Console.WriteLine("Not found"); else Console.WriteLine("Found"); } }

En el ejemplo anterior, se encuentra un error de compilación en la línea if (result == null) .

CS0019 El operador ''=='' no se puede aplicar a los operandos de tipo ''(int a, int b, int c)'' y ''''

¿Cómo puedo verificar si la tupla se encuentra antes de continuar en mi lógica "encontrada"?

Antes de usar las nuevas tuplas c # 7, tendría esto:

class Program { private static readonly List<Tuple<int, int, int>> Map = new List<Tuple<int, int, int>>() { new Tuple<int, int, int> (1, 1, 2), new Tuple<int, int, int> (1, 2, 3), new Tuple<int, int, int> (2, 2, 4) }; static void Main(string[] args) { var result = Map.FirstOrDefault(w => w.Item1 == 4 && w.Item2 == 4); if (result == null) Console.WriteLine("Not found"); else Console.WriteLine("Found"); } }

Lo que funcionó bien. Me gusta la intención más fácilmente interpretada de la nueva sintaxis, pero no estoy seguro de cómo anular la comprobación antes de actuar sobre lo que se encontró (o no).


La mayoría de las respuestas anteriores implican que su elemento resultante no puede ser predeterminado (T), donde T es su clase / tupla.

Una forma simple de evitarlo es utilizar un enfoque como el siguiente:

var result = Map .Select(t => (t, IsResult:true)) .FirstOrDefault(w => w.t.Item1 == 4 && w.t.Item2 == 4); Console.WriteLine(result.IsResult ? "Found" : "Not found");

Este ejemplo utiliza nombres de tuplas implícitos de C # 7.1 (y el paquete ValueTuple para C # 7), pero puede dar el nombre a sus elementos de la tupla explícitamente si es necesario, o usar una tupla simple Tuple<T1,T2> lugar.


Las tuplas de valor son tipos de valor. No pueden ser nulos, por lo que el compilador se queja. El antiguo tipo Tuple era un tipo de referencia.

El resultado de FirstOrDefault() en este caso será una instancia predeterminada de ValueTuple<int,int,int> : todos los campos se establecerán en su valor predeterminado, 0.

Si desea verificar un valor predeterminado, puede comparar el resultado con el valor predeterminado de ValueTuple<int,int,int> , por ejemplo:

var result=(new List<(int a, int b, int c)>() { (1, 1, 2), (1, 2, 3), (2, 2, 4) } ).FirstOrDefault(w => w.a == 4 && w.b == 4); if (result.Equals(default(ValueTuple<int,int,int>))) { Console.WriteLine("Missing!"); }

PALABRA DE ADVERTENCIA

El método se llama FirstOrDefault , no TryFirst . No tiene la intención de verificar si existe un valor o no, aunque todos (ab) lo usamos de esta manera.

Crear tal método de extensión en C # no es tan difícil. La opción clásica es usar un parámetro de salida:

public static bool TryFirst<T>(this IEnumerable<T> seq,Func<T,bool> filter, out T result) { result=default(T); foreach(var item in seq) { if (filter(item)) { result=item; return true; } } return false; }

Llamando a esto se puede simplificar en C # 7 como:

if (myList.TryFirst(w => w.a == 4 && w.b == 1,out var result)) { Console.WriteLine(result); }

Los desarrolladores de F # pueden presumir de tener un Seq.tryPick que devolverá None si no se encuentra ninguna coincidencia.

C # no tiene tipos de opción o el tipo Maybe (todavía), pero quizás (intencionalmente) podemos construir nuestro propio:

class Option<T> { public T Value {get;private set;} public bool HasValue {get;private set;} public Option(T value) { Value=value; HasValue=true;} public static readonly Option<T> Empty=new Option<T>(); private Option(){} public void Deconstruct(out bool hasValue,out T value) { hasValue=HasValue; value=Value; } } public static Option<T> TryPick<T>(this IEnumerable<T> seq,Func<T,bool> filter) { foreach(var item in seq) { if (filter(item)) { return new Option<T>(item); } } return Option<T>.Empty; }

Lo que permite escribir la siguiente llamada al estilo Go:

var (found,value) =myList.TryPick(w => w.a == 4 && w.b == 1);

Además de los más tradicionales:

var result=myList.TryPick(w => w.a == 4 && w.b == 1); if (result.HasValue) {...}


Necesitas:

if (result.Equals(default)) Console.WriteLine(...

(c #> 7.1)


Según lo escrito por Panagiotis, no puedes hacerlo directamente ... Puedes "hacer trampa" un poco:

var result = Map.Where(w => w.a == 4 && w.b == 4).Take(1).ToArray(); if (result.Length == 0) Console.WriteLine("Not found"); else Console.WriteLine("Found");

Tomas hasta un elemento con el Where y pones el resultado en una matriz de longitud 0-1.

Alternativamente podrías repetir la comparación:

var result = Map.FirstOrDefault(w => w.a == 4 && w.b == 4); if (result.a == 4 && result.b == 4) Console.WriteLine("Not found");

Esta segunda opción no funcionará si estuvieras buscando

var result = Map.FirstOrDefault(w => w.a == 0 && w.b == 0);

En este caso, el valor "predeterminado" devuelto por FirstOrDefault() tiene a == 0 y b == 0 .

O simplemente puede crear un FirstOrDefault() "especial" que tenga un out bool success (como los varios TryParse ):

static class EnumerableEx { public static T FirstOrDefault<T>(this IEnumerable<T> source, Func<T, bool> predicate, out bool success) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (predicate == null) { throw new ArgumentNullException(nameof(predicate)); } foreach (T ele in source) { if (predicate(ele)) { success = true; return ele; } } success = false; return default(T); } }

utilízalo como:

bool success; var result = Map.FirstOrDefault(w => w.a == 4 && w.b == 4, out success);

Otro posible método de extensión, ToNullable<>()

static class EnumerableEx { public static IEnumerable<T?> ToNullable<T>(this IEnumerable<T> source) where T : struct { return source.Cast<T?>(); } }

Utilízalo como:

var result = Map.Where(w => w.a == 4 && w.b == 4).ToNullable().FirstOrDefault(); if (result == null)

Tenga en cuenta que el result es una T? , así que tendrás que hacer un resultado. result.Value para usar su valor.


Si está seguro de que su conjunto de datos no incluirá (0, 0, 0) , entonces, como han dicho otros, puede verificar el valor predeterminado:

if (result.Equals(default(ValueTuple<int,int,int>))) ...

Sin embargo, si ese valor puede ocurrir, entonces podrías usar First y atrapar la excepción cuando no haya coincidencia:

class Program { private static readonly List<(int a, int b, int c)> Map = new List<(int a, int b, int c)>() { (1, 1, 2), (1, 2, 3), (2, 2, 4), (0, 0, 0) }; static void Main(string[] args) { try { Map.First(w => w.a == 0 && w.b == 0); Console.WriteLine("Found"); } catch (InvalidOperationException) { Console.WriteLine("Not found"); } } }

Alternativamente, puede usar una biblioteca, como mi propia biblioteca Succinc <T> que proporciona un método TryFirst que devuelve un tipo "quizás" de " none si no coincide, o el elemento si concuerda:

class Program { private static readonly List<(int a, int b, int c)> Map = new List<(int a, int b, int c)>() { (1, 1, 2), (1, 2, 3), (2, 2, 4), (0, 0, 0) }; static void Main(string[] args) { var result = Map.TryFirst(w => w.a == 0 && w.b == 0); Console.WriteLine(result.HasValue ? "Found" : "Not found"); } }


Solo para agregar una alternativa más para tratar con los tipos de valor y FirstOrDefault : use Where y FirstOrDefault el resultado en un tipo anulable:

var result = Map.Where(w => w.a == 4 && w.b == 4) .Cast<(int a, int b, int c)?>().FirstOrDefault(); if (result == null) Console.WriteLine("Not found"); else Console.WriteLine("Found");

Incluso puedes hacer un método de extensión de ello:

public static class Extensions { public static T? StructFirstOrDefault<T>(this IEnumerable<T> items, Func<T, bool> predicate) where T : struct { return items.Where(predicate).Cast<T?>().FirstOrDefault(); } }

Luego, su código original se compilará (suponiendo que reemplace FirstOrDefault con StructFirstOrDefault ).


Su cheque podría ser el siguiente:

if (!Map.Any(w => w.a == 4 && w.b == 4)) { Console.WriteLine("Not found"); } else { var result = Map.First(w => w.a == 4 && w.b == 4); Console.WriteLine("Found"); }


ValueTuple es el tipo subyacente utilizado para las tuplas de C # 7. No pueden ser nulos ya que son tipos de valor. Sin embargo, puede probarlos por defecto, pero eso podría ser un valor válido.

Además, el operador de igualdad no está definido en ValueTuple, por lo que debe usar Equals (...).

static void Main(string[] args) { var result = Map.FirstOrDefault(w => w.Item1 == 4 && w.Item2 == 4); if (result.Equals(default(ValueTuple<int, int, int>))) Console.WriteLine("Not found"); else Console.WriteLine("Found"); }