独特的方式来使用空合并运算符
我知道在C#中使用Null合并运算符的标准方法是设置默认值。
string nobody = null; string somebody = "Bob Saget"; string anybody = ""; anybody = nobody ?? "Mr. T"; // returns Mr. T anybody = somebody ?? "Mr. T"; // returns "Bob Saget"
但还有什么可以??
用于? 它看起来不像三元运算符那样有用,除了比以下更简洁和更易读:
nobody = null; anybody = nobody == null ? "Bob Saget" : nobody; // returns Bob Saget
所以,即使知道更less的空合并操作符…
-
你用过
??
为了别的吗? -
是
??
必要的,或者你应该使用三元运算符(大多数人都熟悉)
那么,首先,比标准三元链更容易链接:
string anybody = parm1 ?? localDefault ?? globalDefault;
与
string anyboby = (parm1 != null) ? parm1 : ((localDefault != null) ? localDefault : globalDefault);
如果null可能的对象不是一个variables,它也可以工作:
string anybody = Parameters["Name"] ?? Settings["Name"] ?? GlobalSetting["Name"];
与
string anybody = (Parameters["Name"] != null ? Parameters["Name"] : (Settings["Name"] != null) ? Settings["Name"] : GlobalSetting["Name"];
我用它作为一个懒惰加载一行:
public MyClass LazyProp { get { return lazyField ?? (lazyField = new MyClass()); } }
可读? 自己决定。
我发现它有两个“有点奇怪”的方法是有用的:
- 作为写入
TryParse
例程时使用out
参数的替代方法(即,如果parsing失败,则返回空值) - 作为一个“不知道”比较代表
后者需要更多的信息。 通常,当您创build一个与多个元素的比较时,您需要查看比较的第一部分(例如年龄)是否给出明确的答案,然后是仅当第一部分没有帮助时才接下来的部分(例如姓名)。 使用空合并运算符意味着您可以编写非常简单的比较(无论是sorting还是相等)。 例如,在MiscUtil中使用几个辅助类:
public int Compare(Person p1, Person p2) { return PartialComparer.Compare(p1.Age, p2.Age) ?? PartialComparer.Compare(p1.Name, p2.Name) ?? PartialComparer.Compare(p1.Salary, p2.Salary) ?? 0; }
无可否认,我现在在MiscUtil中有了ProjectionComparer,还有一些扩展,使得这种事情变得更简单 – 但它仍然很整洁。
在实现Equals之前,检查引用相等(或无效)也是一样的。
另一个优点是三元运算符需要双重评估或临时variables。
考虑到这一点,例如:
string result = MyMethod() ?? "default value";
而与三元运算符,你留下了:
string result = (MyMethod () != null ? MyMethod () : "default value");
这会调用MyMethod两次,或者:
string methodResult = MyMethod (); string result = (methodResult != null ? methodResult : "default value");
无论哪种方式,空合并运算符更清洁,我猜,效率更高。
另一个要考虑的事情是,联合运算符不要像三元组那样两次调用属性的get方法。
所以有些情况下你不应该使用三元,例如:
public class A { var count = 0; private int? _prop = null; public int? Prop { get { ++count; return _prop } set { _prop = value; } } }
如果你使用:
var a = new A(); var b = a.Prop == null ? 0 : a.Prop;
该getter将被调用两次, count
variables将等于2,如果您使用:
var b = a.Prop ?? 0
count
variables将等于1,因为它应该。
我发现的最大优势是??
运算符是,您可以轻松地将可空值types转换为不可空types:
int? test = null; var result = test ?? 0; // result is int, not int?
我经常在Linq查询中使用这个:
Dictionary<int, int?> PurchaseQuantities; // PurchaseQuantities populated via ASP .NET MVC form. var totalPurchased = PurchaseQuantities.Sum(kvp => kvp.Value ?? 0); // totalPurchased is int, not int?
我用过了? 在我的IDataErrorInfo的实现中:
public string Error { get { return this["Name"] ?? this["Address"] ?? this["Phone"]; } } public string this[string columnName] { get { ... } }
如果任何个人财产处于“错误”状态,我得到这个错误,否则我得到空。 工作得很好。
您可以使用null合并运算符来使其更清晰,以处理未设置可选参数的情况:
public void Method(Arg arg = null) { arg = arg ?? Arg.Default; ...
唯一的问题是null-coalesce运算符不检测空string。
即
string result1 = string.empty ?? "dead code!"; string result2 = null ?? "coalesced!";
OUTPUT:
result1 = "" result2 = coalesced!
我目前正在寻找重写? 操作员来解决这个问题。 将它embedded到框架中肯定会很方便。
思考?
是? 必要的,或者你应该使用三元运算符(大多数人都熟悉)
实际上,我的经验是,很less有人熟悉三元运算符(或者更准确地说, 条件运算符; ?:
是“三元”,就像||
是二元的,或者是一元或二元的;然而恰巧是许多语言中唯一的三元运算符),所以至less在这个有限的样本中,你的陈述就在那里失败了。
而且,正如前面所提到的,当一个主要的情况是空合并运算符是非常有用的,那就是每当被评估的expression式都有任何副作用。 在这种情况下,如果没有(a)引入临时variables,或者(b)更改应用程序的实际逻辑, 则不能使用条件运算符。 (b)在任何情况下都显然是不合适的,虽然这是一种个人偏好,但我不喜欢用很多无关的,即使是短暂的variables来夸大声明范围,所以(a)也是特定情况。
当然,如果你需要对结果进行多重检查,条件运算符或者一组if
块可能是工作的工具。 但对于简单的“如果这是空的,使用它,否则使用它”,空合并运算符??
是完美的。
是? 必要的,或者你应该使用三元运算符(大多数人都熟悉)
你应该使用最好的expression你的意图。 由于有一个空的合并运算符,请使用它 。
另一方面,由于它很专业,我认为它没有其他用途。 我会更喜欢适当的重载||
和其他语言一样。 在语言devise上这将更加简单。 但是…
凉! 算我一个不知道空合并操作符的人 – 这是非常漂亮的东西。
我发现比三元操作符更容易阅读。
首先想到的是我可以使用它的所有我的默认参数在一个地方。
public void someMethod( object parm2, ArrayList parm3 ) { someMethod( null, parm2, parm3 ); } public void someMethod( string parm1, ArrayList parm3 ) { someMethod( parm1, null, parm3 ); } public void someMethod( string parm1, object parm2, ) { someMethod( parm1, parm2, null ); } public void someMethod( string parm1 ) { someMethod( parm1, null, null ); } public void someMethod( object parm2 ) { someMethod( null, parm2, null ); } public void someMethod( ArrayList parm3 ) { someMethod( null, null, parm3 ); } public void someMethod( string parm1, object parm2, ArrayList parm3 ) { // Set your default parameters here rather than scattered through the above function overloads parm1 = parm1 ?? "Default User Name"; parm2 = parm2 ?? GetCurrentUserObj(); parm3 = parm3 ?? DefaultCustomerList; // Do the rest of the stuff here }
我喜欢使用null合并运算符来延迟加载某些属性。
一个非常简单的(和人为的)例子只是为了说明我的观点:
public class StackOverflow { private IEnumerable<string> _definitions; public IEnumerable<string> Definitions { get { return _definitions ?? ( _definitions = new List<string> { "definition 1", "definition 2", "definition 3" } ); } } }
有一件事我最近做了很多事情,就是使用null合并备份到as
。 例如:
object boxed = 4; int i = (boxed as int?) ?? 99; Console.WriteLine(i); // Prints 4
这对于备份长连锁也很有用?.
这可能会失败
int result = MyObj?.Prop?.Foo?.Val ?? 4; string other = (MyObj?.Prop?.Foo?.Name as string)?.ToLower() ?? "not there";
一个奇怪的用例位,但我有一个方法,其中一个IDisposable
对象可能作为parameter passing(因此由父处理),但也可以为空(所以应创build和本地方法处置)
要使用它,代码看起来像
Channel channel; Authentication authentication; if (entities == null) { using (entities = Entities.GetEntities()) { channel = entities.GetChannelById(googleShoppingChannelCredential.ChannelId); [...] } } else { channel = entities.GetChannelById(googleShoppingChannelCredential.ChannelId); [...] }
但是,空合并变得更加整齐
using (entities ?? Entities.GetEntities()) { channel = entities.GetChannelById(googleShoppingChannelCredential.ChannelId); [...] }