扩展C#合并运营 [英] Extending the C# Coalesce Operator
问题描述
在我解释一下我想做的事,如果你看看下面的代码,你会明白什么是应该做的?的(更新 - 见下文)的
Console.WriteLine(
Coalesce.UntilNull(getSomeFoo (),F => f.Value) - 默认值);
C#已经有一个空合并运算符的作品相当不错的简单对象,但并没有帮助,如果你需要访问该对象的成员。
例如:
Console.WriteLine(getSomeString()??默认);
工作得很好,但它不会帮助你在这里:
公共类Foo
{
公共美孚(字符串值){值=价值; }
公共字符串值{获得;私人集; }
}
//如果返回null,则此显然会失败
Console.WriteLine(getSomeFoo()值??默认。);
//这是有意
富富= getSomeFoo();
Console.WriteLine(!?富=空foo.Value:默认);
由于这一点是我遇到很多时候我想过使用的扩展方法的(旧版本)的:
公共静态类扩展
{
公共静态TResult凝聚< T,TResult>(这件T OBJ,Func键< T,TResult> FUNC,TResult设置defaultValue)
{
如果(OBJ!= NULL)返回FUNC(OBJ);
,否则返回设置defaultValue;
}
公共静态TResult凝聚< T,TResult>(这件T OBJ,Func键< T,TResult> FUNC,Func键< TResult> defaultFunc)
{
如果(!OBJ = NULL)返回FUNC(OBJ);
否则返回defaultFunc();
}
}
这让我写:
Console.WriteLine(getSomeFoo()COALESCE(F =方式> f.Value,默认值));
所以,你会认为这个代码是可读的? ?是凝聚一个好听的名字
编辑1:去掉括号由马克的建议
更新
我真的很喜欢lassevk的建议和Groo的反馈。所以我加了过载和没有实现它作为一个扩展的方法。我还决定,设置defaultValue是多余的,因为你可以只使用现有的? 。运营商为
这是修改后的类:
公开静态类合并
{
公共静态TResult UntilNull< T,TResult>(T OBJ,Func键< T,TResult> FUNC),其中TResult:类
{
如果(OBJ! = NULL)返回FUNC(OBJ);
,否则返回NULL;
}
公共静态TResult UntilNull&下; T1,T2,TResult>(T1物镜,Func键与所述的T1,T2和GT; func1的,Func键与下; T2,TResult> FUNC2)其中TResult:类
{
如果(!OBJ = NULL)返回UntilNull(func1的(OBJ),FUNC2);
,否则返回NULL;
}
公共静态TResult UntilNull&下; T1,T2,T3,TResult>(T1物镜,Func键与所述的T1,T2和GT; func1的,Func键与下; T2,T3> FUNC2,Func键与下; T3, TResult> FUNC3)其中TResult:类
{
如果(!OBJ = NULL)返回UntilNull(func1的(OBJ),FUNC2,FUNC3);
,否则返回NULL;
}
公共静态TResult UntilNull&下; T1,T2,T3,T4,TResult>(T1物镜,Func键与所述的T1,T2和GT; func1的,Func键与下; T2,T3> FUNC2,Func键&下; T3,T4> FUNC3,Func键< T4,TResult> FUNC4)其中TResult:类
{
如果(!OBJ = NULL)返回UntilNull(func1的(OBJ),FUNC2,FUNC3,FUNC4);
,否则返回NULL;
}
}
使用范例:
Console.WriteLine(
Coalesce.UntilNull(getSomeFoo(),F => f.Value) - 默认值);
另外一个例子:
公共类酒吧
{
公共酒吧儿童{搞定;组; }
公共富富{搞定;组; }
}
吧吧=新的酒吧{儿童=新的酒吧{美孚=新的Foo(值)}};
//输出值:
Console.WriteLine(
Coalesce.UntilNull(巴,B => b.Child,B => b.Foo,女= GT; f.Value) - 空);
//输出空:
Console.WriteLine(
Coalesce.UntilNull(巴,B => b.Foo,F => f.Value)? ? 空值);
六年后和的 空,条件运算符 的位置:
有时代码往往淹没在空检查了一下。在
空条件运算符可以访问成员和元素只有
当接收器是不是空的,否则提供一个空的结果:诠释?长度=客户。长度?; //空,如果客户是空的客户
第一=客户[0]; //空,如果客户是空
的空条件操作便利,连同$ B $使用b空合并运算符??:?
INT长度=客户。长度? 0; // 0,如果客户是空
的空条件运算符具有短路行为,
,其中紧随其后的成员链访问,元素
访问,如果原来的
接收器是不是空的调用才会执行:诠释?第一=客户[0] .Orders.Count()?;
这例子基本上等同于:
诠释?首先=(顾客!= NULL)?客户[0] .Orders.Count():空;
除了客户只计算一次。会员
没有访问,访问的元素和调用紧随?
的,除非客户执行有一个非空值。
当然空有条件运营商可以自行链接,在
情况下,有一个需要在链检查空不止一次:诠释?第一=客户[0] .Orders .Count之间的()?;
注意,调用(带括号的参数列表)不能
紧跟在?操作员 - 这将导致太多
句法歧义。因此,调用
委托,只有当它的存在不能正常工作的简单方法。但是,您可以通过
上的委托Invoke方法做到这一点:如果(谓词.Invoke(E )??假){...}
我们期待这种模式的一个非常普遍的使用将是
触发事件:的PropertyChanged .Invoke(这一点,参数);
这是你之前检查null
触发了一个方便,线程安全的方式事件。它的线程安全的原因是,该功能
评估左侧只有一次,并保持它在一个临时
变量
Before I explain what I want to do, if you look at the following code, would you understand what it's supposed to do? (updated - see below)
Console.WriteLine(
Coalesce.UntilNull(getSomeFoo(), f => f.Value) ?? "default value");
C# already has a null-coalescing operator that works quite well on simple objects but doesn't help if you need to access a member of that object.
E.g.
Console.WriteLine(getSomeString()??"default");
works very well but it won't help you here:
public class Foo
{
public Foo(string value) { Value=value; }
public string Value { get; private set; }
}
// this will obviously fail if null was returned
Console.WriteLine(getSomeFoo().Value??"default");
// this was the intention
Foo foo=getSomeFoo();
Console.WriteLine(foo!=null?foo.Value:"default");
Since this is something that I come across quite often I thought about using an extension method (old version):
public static class Extension
{
public static TResult Coalesce<T, TResult>(this T obj, Func<T, TResult> func, TResult defaultValue)
{
if (obj!=null) return func(obj);
else return defaultValue;
}
public static TResult Coalesce<T, TResult>(this T obj, Func<T, TResult> func, Func<TResult> defaultFunc)
{
if (obj!=null) return func(obj);
else return defaultFunc();
}
}
Which allows me to write:
Console.WriteLine(getSomeFoo().Coalesce(f => f.Value, "default value"));
So would you consider this code to be readable? Is Coalesce a good name?
Edit 1: removed the brackets as suggested by Marc
Update
I really liked lassevk's suggestions and Groo's feedback. So I added overloads and didn't implement it as an extension method. I also decided that defaultValue was redundant because you could just use the existing ?? operator for that.
This is the revised class:
public static class Coalesce
{
public static TResult UntilNull<T, TResult>(T obj, Func<T, TResult> func) where TResult : class
{
if (obj!=null) return func(obj);
else return null;
}
public static TResult UntilNull<T1, T2, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, TResult> func2) where TResult : class
{
if (obj!=null) return UntilNull(func1(obj), func2);
else return null;
}
public static TResult UntilNull<T1, T2, T3, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, T3> func2, Func<T3, TResult> func3) where TResult : class
{
if (obj!=null) return UntilNull(func1(obj), func2, func3);
else return null;
}
public static TResult UntilNull<T1, T2, T3, T4, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, T3> func2, Func<T3, T4> func3, Func<T4, TResult> func4) where TResult : class
{
if (obj!=null) return UntilNull(func1(obj), func2, func3, func4);
else return null;
}
}
Sample usage:
Console.WriteLine(
Coalesce.UntilNull(getSomeFoo(), f => f.Value) ?? "default value");
Another sample:
public class Bar
{
public Bar Child { get; set; }
public Foo Foo { get; set; }
}
Bar bar=new Bar { Child=new Bar { Foo=new Foo("value") } };
// prints "value":
Console.WriteLine(
Coalesce.UntilNull(bar, b => b.Child, b => b.Foo, f => f.Value) ?? "null");
// prints "null":
Console.WriteLine(
Coalesce.UntilNull(bar, b => b.Foo, f => f.Value) ?? "null");
Six years later and Null-conditional operators are here:
Sometimes code tends to drown a bit in null-checking. The null-conditional operator lets you access members and elements only when the receiver is not-null, providing a null result otherwise:
int? length = customers?.Length; // null if customers is null Customer first = customers?[0]; // null if customers is null
The null-conditional operator is conveniently used together with the null coalescing operator ??:
int length = customers?.Length ?? 0; // 0 if customers is null
The null-conditional operator exhibits short-circuiting behavior, where an immediately following chain of member accesses, element accesses and invocations will only be executed if the original receiver was not null:
int? first = customers?[0].Orders.Count();
This example is essentially equivalent to:
int? first = (customers != null) ? customers[0].Orders.Count() : null;
Except that customers is only evaluated once. None of the member accesses, element accesses and invocations immediately following the ? are executed unless customers has a non-null value.
Of course null-conditional operators can themselves be chained, in case there is a need to check for null more than once in a chain:
int? first = customers?[0].Orders?.Count();
Note that an invocation (a parenthesized argument list) cannot immediately follow the ? operator – that would lead to too many syntactic ambiguities. Thus, the straightforward way of calling a delegate only if it’s there does not work. However, you can do it via the Invoke method on the delegate:
if (predicate?.Invoke(e) ?? false) { … }
We expect that a very common use of this pattern will be for triggering events:
PropertyChanged?.Invoke(this, args);
This is an easy and thread-safe way to check for null before you trigger an event. The reason it’s thread-safe is that the feature evaluates the left-hand side only once, and keeps it in a temporary variable.
这篇关于扩展C#合并运营的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!