C# 中如果不为 null 则调用方法

发布于 2024-07-19 19:55:03 字数 355 浏览 9 评论 0原文

是否有可能以某种方式缩短这个声明?

if (obj != null)
    obj.SomeMethod();

因为我碰巧写了很多这样的东西,这很烦人。 我唯一能想到的就是实现空对象模式,但这不是我每次都能做的,而且它当然不是缩短语法的解决方案。

与事件类似的问题,

public event Func<string> MyEvent;

然后调用

if (MyEvent != null)
    MyEvent.Invoke();

Is it possible to somehow shorten this statement?

if (obj != null)
    obj.SomeMethod();

because I happen to write this a lot and it gets pretty annoying. The only thing I can think of is to implement Null Object pattern, but that's not what I can do every time and it's certainly not a solution to shorten syntax.

And similar problem with events, where

public event Func<string> MyEvent;

and then invoke

if (MyEvent != null)
    MyEvent.Invoke();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

久伴你 2024-07-26 19:55:03

从 C# 6 开始,您可以只使用:

MyEvent?.Invoke();

或:

obj?.SomeMethod();

?. 是 null 传播运算符,并且会导致 .Invoke() 在以下情况下短路:操作数为null。 操作数仅被访问一次,因此不存在“检查和调用之间的值变化”问题的风险。

===

在 C# 6 之前,不存在:没有空安全魔法,但有一个例外; 扩展方法 - 例如:

public static void SafeInvoke(this Action action) {
    if(action != null) action();
}

现在这是有效的:

Action act = null;
act.SafeInvoke(); // does nothing
act = delegate {Console.WriteLine("hi");}
act.SafeInvoke(); // writes "hi"

在事件的情况下,这具有还删除竞争条件的优点,即您不需要临时变量。 所以通常你需要:

var handler = SomeEvent;
if(handler != null) handler(this, EventArgs.Empty);

但使用:

public static void SafeInvoke(this EventHandler handler, object sender) {
    if(handler != null) handler(sender, EventArgs.Empty);
}

我们可以简单地使用:

SomeEvent.SafeInvoke(this); // no race condition, no null risk

From C# 6 onwards, you can just use:

MyEvent?.Invoke();

or:

obj?.SomeMethod();

The ?. is the null-propagating operator, and will cause the .Invoke() to be short-circuited when the operand is null. The operand is only accessed once, so there is no risk of the "value changes between check and invoke" problem.

===

Prior to C# 6, no: there is no null-safe magic, with one exception; extension methods - for example:

public static void SafeInvoke(this Action action) {
    if(action != null) action();
}

now this is valid:

Action act = null;
act.SafeInvoke(); // does nothing
act = delegate {Console.WriteLine("hi");}
act.SafeInvoke(); // writes "hi"

In the case of events, this has the advantage of also removing the race-condition, i.e. you don't need a temporary variable. So normally you'd need:

var handler = SomeEvent;
if(handler != null) handler(this, EventArgs.Empty);

but with:

public static void SafeInvoke(this EventHandler handler, object sender) {
    if(handler != null) handler(sender, EventArgs.Empty);
}

we can use simply:

SomeEvent.SafeInvoke(this); // no race condition, no null risk
陌若浮生 2024-07-26 19:55:03

您正在寻找的是 空条件(不是“合并”)运算符:?.。 它从 C# 6 开始可用。

您的示例为 obj?.SomeMethod();。 如果 obj 为 null,则不会发生任何事情。 当方法有参数时,例如 obj?.SomeMethod(new Foo(), GetBar()); 如果 obj 为 null,则不会评估参数,这在评估时很重要这些论点会产生副作用。

并且链接是可能的:myObject?.Items?[0]?.DoSomething()

What you're looking for is the Null-Conditional (not "coalescing") operator: ?.. It's available as of C# 6.

Your example would be obj?.SomeMethod();. If obj is null, nothing happens. When the method has arguments, e.g. obj?.SomeMethod(new Foo(), GetBar()); the arguments are not evaluated if obj is null, which matters if evaluating the arguments would have side effects.

And chaining is possible: myObject?.Items?[0]?.DoSomething()

黎夕旧梦 2024-07-26 19:55:03

快速扩展方法:

    public static void IfNotNull<T>(this T obj, Action<T> action, Action actionIfNull = null) where T : class {
        if(obj != null) {
            action(obj);
        } else if ( actionIfNull != null ) {
            actionIfNull();
        }
    }

example:

  string str = null;
  str.IfNotNull(s => Console.Write(s.Length));
  str.IfNotNull(s => Console.Write(s.Length), () => Console.Write("null"));

或者:

    public static TR IfNotNull<T, TR>(this T obj, Func<T, TR> func, Func<TR> ifNull = null) where T : class {
        return obj != null ? func(obj) : (ifNull != null ? ifNull() : default(TR));
    }

example:

    string str = null;
    Console.Write(str.IfNotNull(s => s.Length.ToString());
    Console.Write(str.IfNotNull(s => s.Length.ToString(), () =>  "null"));

A quick extension method:

    public static void IfNotNull<T>(this T obj, Action<T> action, Action actionIfNull = null) where T : class {
        if(obj != null) {
            action(obj);
        } else if ( actionIfNull != null ) {
            actionIfNull();
        }
    }

example:

  string str = null;
  str.IfNotNull(s => Console.Write(s.Length));
  str.IfNotNull(s => Console.Write(s.Length), () => Console.Write("null"));

or alternatively:

    public static TR IfNotNull<T, TR>(this T obj, Func<T, TR> func, Func<TR> ifNull = null) where T : class {
        return obj != null ? func(obj) : (ifNull != null ? ifNull() : default(TR));
    }

example:

    string str = null;
    Console.Write(str.IfNotNull(s => s.Length.ToString());
    Console.Write(str.IfNotNull(s => s.Length.ToString(), () =>  "null"));
咽泪装欢 2024-07-26 19:55:03

事件可以使用空的默认委托进行初始化,该委托永远不会被删除:

public event EventHandler MyEvent = delegate { };

无需进行空检查。

[更新,感谢 Bevan 指出这一点]

不过,请注意可能的性能影响。 我所做的一个快速微基准测试表明,使用“默认委托”模式时,处理没有订阅者的事件会慢 2-3 倍。 (在我的双核 2.5GHz 笔记本电脑上,这意味着 279 毫秒:筹集 5000 万个未订阅事件需要 785 毫秒。)。 对于应用程序热点,这可能是一个需要考虑的问题。

Events can be initialized with an empty default delegate which is never removed:

public event EventHandler MyEvent = delegate { };

No null-checking necessary.

[Update, thanks to Bevan for pointing this out]

Be aware of the possible performance impact, though. A quick micro benchmark I did indicates that handling an event with no subscribers is 2-3 times slower when using the the "default delegate" pattern. (On my dual core 2.5GHz laptop that means 279ms : 785ms for raising 50 million not-subscribed events.). For application hot spots, that might be an issue to consider.

烟酒忠诚 2024-07-26 19:55:03

是的,在 C# 6.0 中 -- https://msdn.microsoft.com/en-我们/杂志/dn802602.aspx

object?.SomeMethod()

Yes, in C# 6.0 -- https://msdn.microsoft.com/en-us/magazine/dn802602.aspx.

object?.SomeMethod()
就此别过 2024-07-26 19:55:03

Ian Griffiths 的这篇文章给出了两种不同的方法他总结出的问题解决方案是你不应该使用的巧妙技巧。

This article by Ian Griffiths gives two different solutions to the problem that he concludes are neat tricks that you should not use.

靑春怀旧 2024-07-26 19:55:03

像有人建议的那样,建立扩展方法并不能真正解决竞争条件问题,而是隐藏它们。

public static void SafeInvoke(this EventHandler handler, object sender)
{
    if (handler != null) handler(sender, EventArgs.Empty);
}

如前所述,这段代码与临时变量的解决方案是优雅的等价物,但是...

两者的问题事件的订阅者可能会在取消事件订阅后被调用。 这是可能的,因为取消订阅可能发生在将委托实例复制到临时变量(或作为上述方法中的参数传递)之后、调用委托之前。

一般来说,在这种情况下客户端代码的行为是不可预测的:组件状态已经不允许处理事件通知。 可以以处理它的方式编写客户端代码,但这会给客户端带来不必要的责任。

确保线程安全的唯一已知方法是对事件发送者使用锁定语句。 这可确保所有订阅\取消订阅\调用均已序列化。

为了更准确,锁定应该应用于添加\删除事件访问器方法中使用的同一同步对象,默认为“this”。

Cerating extention method like one suggested does not really solve issues with race conditions, but rather hide them.

public static void SafeInvoke(this EventHandler handler, object sender)
{
    if (handler != null) handler(sender, EventArgs.Empty);
}

As stated this code is the elegant equivalent to solution with temporary variable, but...

The problem with both that it's possible that subsciber of the event could be called AFTER it has unsubscribed from the event. This is possible because unsubscription can happen after delegate instance is copied to the temp variable (or passed as parameter in the method above), but before delegate is invoked.

In general the behaviour of the client code is unpredictable in such case: component state could not allow to handle event notification already. It's possible to write client code in the way to handle it, but it would put unnecesssary responsibility to the client.

The only known way to ensure thread safity is to use lock statement for the sender of the event. This ensures that all subscriptions\unsubscriptions\invocation are serialized.

To be more accurate lock should be applied to the same sync object used in add\remove event accessor methods which is be default 'this'.

一个人的旅程 2024-07-26 19:55:03

我同意肯尼·埃利亚松的回答。 使用扩展方法。 以下是扩展方法和所需的 IfNotNull 方法的简要概述。

扩展方法( IfNotNull 方法)

I agree with the answer by Kenny Eliasson. Go with Extension methods. Here is a brief overview of extension methods and your required IfNotNull method.

Extension Methods ( IfNotNull method )

柏拉图鍀咏恒 2024-07-26 19:55:03

也许不是更好,但在我看来更具可读性的是创建一个扩展方法

public static bool IsNull(this object obj) {
 return obj == null;
}

Maybe not better but in my opinion more readable is to create an extension method

public static bool IsNull(this object obj) {
 return obj == null;
}
鲸落 2024-07-26 19:55:03

我已经制作了我使用的通用扩展。

public static class ObjectExtensions {
    public static void With<T>(this T value, Action<T> todo) {
        if (value != null) todo(value);
    }
}

然后我像下面一样使用它。

string myString = null;
myString.With((value) => Console.WriteLine(value)); // writes nothing
myString = "my value";
myString.With((value) => Console.WriteLine(value)); // Writes `my value`

I have made this generic extension that I use.

public static class ObjectExtensions {
    public static void With<T>(this T value, Action<T> todo) {
        if (value != null) todo(value);
    }
}

Then I use it like below.

string myString = null;
myString.With((value) => Console.WriteLine(value)); // writes nothing
myString = "my value";
myString.With((value) => Console.WriteLine(value)); // Writes `my value`
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文