鉴于:
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");
}
}
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,遇到编译器错误if (result == null).
CS0019运算符'=='不能应用于'(int a,int b,int c)'和''类型的操作数
在继续执行"找到"逻辑之前,我该如何检查是否找到了元组?
在使用新的c#7元组之前,我会这样:
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");
}
}
Run Code Online (Sandbox Code Playgroud)
哪个工作正常.我喜欢新语法更容易解释的意图,但我不确定如何在对发现的内容(或不发现)进行操作之前对其进行null检查.
Pan*_*vos 50
值元组是值类型.它们不能为null,这就是编译器抱怨的原因.旧的Tuple类型是引用类型
FirstOrDefault()在这种情况下,结果将是 - 的默认实例ValueTuple<int,int,int>- 所有字段都将设置为其默认值0.
如果要检查默认值,可以将结果与默认值进行比较ValueTuple<int,int,int>,例如:
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!");
}
Run Code Online (Sandbox Code Playgroud)
警告的词
该方法被调用FirstOrDefault,而不是TryFirst.它并不意味着要检查一个值是否存在,尽管我们都(ab)以这种方式使用它.
在C#中创建这样的扩展方法并不困难.经典选项是使用out参数:
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;
}
Run Code Online (Sandbox Code Playgroud)
在C#7中可以简化调用此操作:
if (myList.TryFirst(w => w.a == 4 && w.b == 1,out var result))
{
Console.WriteLine(result);
}
Run Code Online (Sandbox Code Playgroud)
F#开发人员可以吹嘘他们有一个Seq.tryPick,None如果找不到匹配项将返回.
C#没有Option类型或Maybe类型(还),但也许(双关语)我们可以自己构建:
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;
}
Run Code Online (Sandbox Code Playgroud)
这允许编写以下Go样式调用:
var (found,value) =myList.TryPick(w => w.a == 4 && w.b == 1);
Run Code Online (Sandbox Code Playgroud)
除了更传统的:
var result=myList.TryPick(w => w.a == 4 && w.b == 1);
if (result.HasValue) {...}
Run Code Online (Sandbox Code Playgroud)
Des*_*ond 17
在 C# 7.3 中,它非常干净:
var result = Map.FirstOrDefault(w => w.a == 4 && w.b == 4);
if (result == default) {
Console.WriteLine("Not found");
} else {
Console.WriteLine("Found");
}
Run Code Online (Sandbox Code Playgroud)
Evk*_*Evk 15
只是添加一个替代方法来处理值类型和FirstOrDefault:使用Where并将结果转换为可空类型:
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");
Run Code Online (Sandbox Code Playgroud)
你甚至可以制作它的扩展方法:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
然后你原来的代码将编译(假设您更换FirstOrDefault同StructFirstOrDefault).
由Panagiotis写的你不能直接做到......你可以"欺骗"一点:
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");
Run Code Online (Sandbox Code Playgroud)
最多使用一个元素,Where并将结果放入长度为0-1的数组中.
或者你可以重复比较:
var result = Map.FirstOrDefault(w => w.a == 4 && w.b == 4);
if (result.a == 4 && result.b == 4)
Console.WriteLine("Not found");
Run Code Online (Sandbox Code Playgroud)
如果您正在寻找,第二个选项将无效
var result = Map.FirstOrDefault(w => w.a == 0 && w.b == 0);
Run Code Online (Sandbox Code Playgroud)
在这种情况下,FirstOrDefault() has a == 0和返回的"默认"值b == 0.
或者你可以简单地创建一个FirstOrDefault()具有out bool success(像各种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);
}
}
Run Code Online (Sandbox Code Playgroud)
使用它像:
bool success;
var result = Map.FirstOrDefault(w => w.a == 4 && w.b == 4, out success);
Run Code Online (Sandbox Code Playgroud)
其他可能的扩展方法, ToNullable<>()
static class EnumerableEx
{
public static IEnumerable<T?> ToNullable<T>(this IEnumerable<T> source) where T : struct
{
return source.Cast<T?>();
}
}
Run Code Online (Sandbox Code Playgroud)
使用它像:
var result = Map.Where(w => w.a == 4 && w.b == 4).ToNullable().FirstOrDefault();
if (result == null)
Run Code Online (Sandbox Code Playgroud)
请注意,这result是一个T?,因此您需要result.Value使用它的值.
如果您确定您的数据集不包含(0, 0, 0),那么正如其他人所说,您可以检查默认值:
if (result.Equals(default(ValueTuple<int,int,int>))) ...
Run Code Online (Sandbox Code Playgroud)
如果该值可能会出现,那么您可以使用First并在没有匹配时捕获异常:
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");
}
}
}
Run Code Online (Sandbox Code Playgroud)
或者,你可以使用一个库,比如我自己的Succinc <T>库,它提供了一种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)
{
var result = Map.TryFirst(w => w.a == 0 && w.b == 0);
Console.WriteLine(result.HasValue ? "Found" : "Not found");
}
}
Run Code Online (Sandbox Code Playgroud)
你需要:
if (result.Equals(default)) Console.WriteLine(...
Run Code Online (Sandbox Code Playgroud)
(c# > 7.1)
您的支票可能如下:
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");
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
11477 次 |
| 最近记录: |