[英]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.