如何在LINQ查询中对c#7元组进行空检查?
鉴于:如何在LINQ查询中对c#7元组进行空检查?,c#,linq,c#-7.0,C#,Linq,C# 7.0,鉴于: 效果很好。我喜欢新语法更容易解释的意图,但我不确定在对发现的内容(或未发现的内容)采取行动之前如何对其进行null检查。值元组是值类型。它们不能为null,这就是编译器抱怨的原因。旧元组类型是引用类型 在这种情况下,FirstOrDefault()的结果将是ValueTuple的默认实例-所有字段都将设置为其默认值0 如果要检查默认值,可以将结果与ValueTuple的默认值进行比较,例如: class Program { private static readonly List
效果很好。我喜欢新语法更容易解释的意图,但我不确定在对发现的内容(或未发现的内容)采取行动之前如何对其进行null检查。值元组是值类型。它们不能为null,这就是编译器抱怨的原因。旧元组类型是引用类型 在这种情况下,
FirstOrDefault()
的结果将是ValueTuple
的默认实例-所有字段都将设置为其默认值0
如果要检查默认值,可以将结果与ValueTuple
的默认值进行比较,例如:
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");
}
}
在C#7中可以将此调用简化为:
F#开发人员可以吹嘘,如果没有找到匹配项,他们有一个将返回None
C#没有选项类型或Maybe类型(尚未),但也许(双关语)我们可以构建自己的:
if (myList.TryFirst(w => w.a == 4 && w.b == 1,out var result))
{
Console.WriteLine(result);
}
除了更传统的:
var (found,value) =myList.TryPick(w => w.a == 4 && w.b == 1);
正如Panagiotis所写,你不能直接这么做。。。你可以“欺骗”一点:
var result=myList.TryPick(w => w.a == 4 && w.b == 1);
if (result.HasValue) {...}
使用Where
最多取一个元素,并将结果放入长度为0-1的数组中
或者,您可以重复比较:
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");
如果您正在寻找,第二个选项将不起作用
var result = Map.FirstOrDefault(w => w.a == 4 && w.b == 4);
if (result.a == 4 && result.b == 4)
Console.WriteLine("Not found");
在这种情况下,FirstOrDefault()
返回的“默认”值具有a==0
和b==0
或者,您可以简单地创建一个“特殊的”FirstOrDefault()
,它具有out bool success
(如各种TryParse
):
其他可能的扩展方法,ToNullable()
请注意,
result
是一个T?
,因此您需要执行result.Value
以使用其值。您的检查可以是:
var result = Map.Where(w => w.a == 4 && w.b == 4).ToNullable().FirstOrDefault();
if (result == null)
只需再添加一个选项来处理值类型和
FirstOrDefault
:使用Where
并将结果强制转换为可为null的类型:
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");
}
var result=Map.Where(w=>w.a==4&&w.b==4)
.Cast().FirstOrDefault();
如果(结果==null)
控制台。写入线(“未找到”);
其他的
控制台。写入线(“找到”);
您甚至可以对其进行扩展:
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");
公共静态类扩展{
公共静态T?StructFirstOrDefault(此IEnumerable items,Func谓词),其中T:struct{
返回items.Where(谓词).Cast().FirstOrDefault();
}
}
然后您的原始代码将被编译(假设您将
FirstOrDefault
替换为StructFirstOrDefault
)。ValueTuple是用于C#7元组的基础类型。它们不能为null,因为它们是值类型。您可以测试它们的默认值,但这实际上可能是一个有效值
此外,ValueTuple上未定义相等运算符,因此必须使用Equals(…)
static void Main(字符串[]args)
{
var result=Map.FirstOrDefault(w=>w.Item1==4&&w.Item2==4);
if(result.Equals(默认值(ValueTuple)))
控制台。写入线(“未找到”);
其他的
控制台。写入线(“找到”);
}
如果您确定您的数据集不包含(0,0,0)
,那么正如其他人所说,您可以检查默认值:
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");
}
或者,您可以使用一个库,该库提供一个TryFirst
方法,如果不匹配,则返回一个“可能”类型的none
,如果匹配,则返回该项:
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");
}
}
}
类程序
{
专用静态只读列表映射=
新名单()
{
(1, 1, 2),
(1, 2, 3),
(2, 2, 4),
(0, 0, 0)
};
静态void Main(字符串[]参数)
{
var result=Map.TryFirst(w=>w.a==0&&w.b==0);
Console.WriteLine(result.HasValue?“Found”:“notfound”);
}
}
以上大多数答案都意味着生成的元素不能是默认值(T),其中T是类/元组
一个简单的解决方法是使用以下方法:
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");
}
}
此示例使用C#7.1隐式元组名称(以及C#7的ValueTuple包),但如果需要,您可以显式地为元组元素指定名称,或者使用简单的元组。您需要:
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");
(c#>7.1)我是如何用c#7.3做到的
在C#7.3中,它非常干净:
T findme;
var tuple = list.Select((x, i) => (Item: x, Index: i)).FirstOrDefault(x => x.Item.GetHashCode() == findme.GetHashCode());
if (tuple.Equals(default))
return;
...
var index = tuple.Index;
值元组是值类型。它们不能为null在这种情况下,默认值将是3个零的元组,而不是null
。所以您可以检查它。@juharr但如果列表包含(0,0,0)项,这将中断,因此不太可靠。@Evk(0,0,0)在这种情况下与谓词不匹配,但在其他情况下可能会出现问题。@PanagiotisKanavos不正确。如果colleciton包含(0,0,0)且谓词与之匹配,该怎么办?然后,您必须使用Any
进行预先检查,或者使用Take(1).ToArray()
执行xanatos的技巧,以区分未找到和已找到,但恰好与默认值匹配。但是发布的LINQ查询显然不会在列表中找到一个元组,该元组的a
和b
值为4,那么我该如何验证呢?你会得到一个默认值——一个所有字段都设置为默认值的元组,即0。这可能是映射
中的一个有效案例,我实际上并没有使用几个int
,它只是作为一个示例。@Kritner那么问题是什么FirstOrDefault
返回第一个值或默认值。正如列表
将返回0(ints的默认值),在这种情况下,您将得到一个默认结构,即在C#7.1中所有字段都设置为默认值的ValueTuple,现在您可以只写:如果(result.Equals(default))
,它将自动推断默认值的类型。要使用C#7.1,您需要Visual Studio 2017的最新版本,并在项目构建设置中将C#版本设置为7.1(Advanced…
)。我认为您不需要在第一个if
中使用结果=
。您的语法有点错误,第一个if应该是if(Map.Any(…)
,b
var result = Map.Where(w => w.a == 4 && w.b == 4).ToNullable().FirstOrDefault();
if (result == null)
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");
}
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");
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();
}
}
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");
}
if (result.Equals(default(ValueTuple<int,int,int>))) ...
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");
}
}
}
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");
}
}
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");
if (result.Equals(default)) Console.WriteLine(...
T findme;
var tuple = list.Select((x, i) => (Item: x, Index: i)).FirstOrDefault(x => x.Item.GetHashCode() == findme.GetHashCode());
if (tuple.Equals(default))
return;
...
var index = tuple.Index;
var result = Map.FirstOrDefault(w => w.a == 4 && w.b == 4);
if (result == default) {
Console.WriteLine("Not found");
} else {
Console.WriteLine("Found");
}