繁体   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