[英]Left side object is not null operator in one line
我有这段代码使用FirstOrDefault()
查找可能存在或不存在的项目:
public class Foo
{
public int Id { get; set; }
public string Bar { get; set; }
}
var items = new List<Foo>
{
new Foo { Id = 1, Bar = "Bar" }
};
var item = items.FirstOrDefault(i => i.Id == 1);
if (item != null)
{
item.Bar = "Baz";
}
有没有办法从最后四行中制作一个单行线,像这样?
items.FirstOrDefault(i => i.Id == 1)?.Bar = "Baz";
这会产生编译器错误:
CS9030:赋值的左侧不能包含 null 传播运算符
您可以使用模式匹配来缩短它:
if (items.FirstOrDefault(i => i.Id == 1) is Foo f)
{
f.Bar = "Qux";
}
因为当FirstOrDefault()
返回null
时,这否定为false
。
与c# 中的一行声明、比较和返回有些相关。
这是一个选项。
items.Where(i => i.Id == 1).Take(1).ToList().ForEach(f => f.Bar = "Baz" );
var items = new List<Foo>
{
new Foo { Id = 1, Bar = "Bar" },
new Foo { Id = 1, Bar = "Bar again!" }
};
items.Where(i => i.Id == 1).Take(1).ToList().ForEach(f => f.Bar = "Baz" );
Console.WriteLine(string.Join(", ", items.Select(i => i.Bar)));
Baz, Bar again!
您可以将此 simplay 封装到 function 中,然后将其用作单线。
private static void SetCountToOneIfExists(this IEnumerable<Foo> items, int id)
{
var item = items.FirstOrDefault(i => i.Id == id);
if(item != null)
item.Bar = 1;
}
items.SetCountToOneIfExists(1);
在我的示例中,我使用了扩展方法,您也可以使用普通方法( private void SetCountToOneIfExists(IEnumerable<Foo> items, int Id)
)。
如果该项目不存在或如何处理,是否应该抛出异常是另一回事。
如果你修改Foo
,你可以。
public class Foo
{
public int Id { get; set; }
public string Bar { get; set; }
internal string SetBar(string v) => Bar = v;
}
public static void Main(string[] args)
{
var items = new List<Foo>{ new Foo { Id = 1, Bar = "Bar" } };
items.FirstOrDefault(i => i.Id == 1)?.SetBar("Baz");
}
作为记录,编译器将拒绝尝试直接调用 set_Bar`
items.FirstOrDefault(i => i.Id == 1)?.set_Bar("Baz");
// 'Program.Foo.Bar.set': cannot explicitly call operator or accessor (CS0571)
(items.FirstOrDefault(x => x.Id == 1) ?? new Foo()).Bar = "Baz";
你甚至可以创建一个扩展,我认为它应该是可重用的:
public static T FirstOrNew<T>(this IEnumerable<T> lst, Func<T, bool> predicate)
=> Enumerable.FirstOrDefault(lst, predicate) ?? default;
那么你的用法是:
items.FirstOrNew(x => x.Id == 1).Bar = "Baz";
这是模式匹配解决方案的更安全版本。 这个版本可以抵抗改变items
持有的类型。 我称之为无铸造解决方案。
if( items.Where(i => i.Id == 1).Take(1) is var x && x.Any()) { x.First().Bar = "Baz"; }
var items = new List<Foo>
{
new Foo { Id = 1, Bar = "Bar" },
new Foo { Id = 1, Bar = "Bar again!" }
};
if( items.Where(i => i.Id == 1).Take(1) is var x && x.Any()) { x.First().Bar = "Baz"; }
Console.WriteLine(string.Join(", ", items.Select(i => i.Bar)));
//Not found? No problem (i.e. no exception)
if( items.Where(i => i.Id == 2).Take(1) is var x2 && x2.Any()) { x2.First().Bar = "Baz2"; }
Output
Baz, Bar again!
我个人的偏好是保留 null 检查,因为它清晰易懂。
但是,如果您的偏好是将检查和分配减少到单行,您可以创建一个通用扩展方法:
public static class NullExtensions
{
public static void UpdateIfNotNull<T>(this T item, Action<T> update)
where T : class
{
if (item != null)
{
update(item);
}
}
}
并按如下方式使用它:
public class Foo
{
public int Bar { get; set; }
}
...
var foo = new Foo();
//One-liner
foo.UpdateIfNotNull(f => f.Bar = 2);
您可以使用它,但如果列表中没有具有指定 id 的项目,则在一行代码中执行多个操作时,它会引发异常:
items.Where(i => i.Id == 1).ToList()[0].Count = 1;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.