簡體   English   中英

左側 object 不是 null 運算符在一行

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

作為記錄,編譯器將拒絕嘗試直接調用 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM